Spring源码二IOC容器源码

news2025/1/18 8:38:27

文章目录

    • Spring IOC初始化
    • 源码剖析
      • 1.prepareRefresh
      • 2.obtainFreshBeanFactory
      • 3.prepareBeanFactory
      • 4.postProcessBeanFactory
      • 5.invokeBeanFactoryPostProcessors
      • 6.registerBeanPostProcessors
      • 7.initMessageSource
      • 8.initApplicationEventMulticaster
      • 9.onRefresh
      • 10.registerListeners
      • 11.finishBeanFactoryInitialization
        • 11.1createBeanInstance
        • 11.2populateBean
        • 11.3initializeBean
      • 12.finishRefresh
      • 13.销毁阶段

Spring IOC初始化

ApplicationContext 是 Spring 的核心接口,表示 Spring 的 IOC 容器,以下三个类是ApplicationContext 接口的常用实现类:ClassPathXmlApplicationContext,AnnotationConfigApplicationContext,FileSystemXmlApplicationContext。

  • ClassPathXmlApplicationContext:这个实现类用于从 classpath 中加载 XML 配置文件来初始化 IoC 容器。
  • AnnotationConfigApplicationContext:这个实现类用于从 Java 配置类如带有@Configuration 注解的类中加载配置信息来初始化 IoC 容器。
  • FileSystemXmlApplicationContext:这个实现类用于从文件系统的指定路径加载 XML 配置文件来初始化 IoC容器。

本文采用第一种方式来初始化IOC容器,首先引入Spring5.3.27版本的依赖,不同版本的实现略有差异,但整体实现逻辑是相同的。

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.27</version>
    </dependency>

创建一个接口CourseService

@Service
public interface CourseService {
    String getCourseName();
}

接口的实现类CourseServiceImpl

public class CourseServiceImpl implements CourseService {
    @Override
    public String getCourseName() {
        return "Spring IOC源码";
    }
}

配置文件application.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="courseService" class="com.czl.spring.CourseServiceImpl"/>
</beans>

启动测试类TestSpring

public class TestSpring {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");

        System.out.println("启动Spring IOC");
        CourseService courseService = applicationContext.getBean(CourseService.class);
        System.out.println(courseService.getCourseName());

    }
}

运行结果
在这里插入图片描述

源码剖析

首先从这行代码ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");进行debug,

	public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
		this(new String[] {configLocation}, true, null);
	}

主要执行以下三个方法

	public ClassPathXmlApplicationContext(
			String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
			throws BeansException {

		super(parent);
		setConfigLocations(configLocations);
		if (refresh) {
			refresh();
		}
	}
  1. super(parent):调用父类的构造方法super(parent),通过创建PathMatchingResourcePatternResolver来设置Bean的资源加载器。
  2. setConfigLocations(configLocations):将Bean的定义资源文件添加到当前的执行程序中。
//解析配置文件的路径,处理多个资源文件可以用字符串数组  
public void setConfigLocations(@Nullable String... locations) {
		if (locations != null) {
			Assert.noNullElements(locations, "Config locations must not be null");
			this.configLocations = new String[locations.length];
			for (int i = 0; i < locations.length; i++) {
				this.configLocations[i] = resolvePath(locations[i]).trim();
			}
		}
		else {
			this.configLocations = null;
		}
	}

通过源码可以看出,可以使用一个字符串数组来配置多个Spring 的配置文件,比如下面通过字符串数组可以直接配置两个XML配置文件。

 String []strXML ={"application.xml","application2.xml"};
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(strXML);
  1. refresh():用于初始化和刷新IoC容器,实现bean的注册、实例化和初始化等过程,最终创建出一个完整的Bean对象。

所有的核心方法都在refresh()中实现。

public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");

		 //执行容器刷新前的方法
	     prepareRefresh();

			// 将BeanDefinition注册到DefaultListableBeanFactory的beanDefinitionMap中
		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
			// 为beanFactory设置一些默认的属性和方法
         prepareBeanFactory(beanFactory);

      try {
         // 子类对Bean工厂进行后处理
         postProcessBeanFactory(beanFactory);

         // 调用注册为Bean的工厂处理器
         invokeBeanFactoryPostProcessors(beanFactory);

         // 解析Bean创建的Bean前后处理器
         registerBeanPostProcessors(beanFactory);
         // 结束Bean工厂后处理器的处理
         beanPostProcess.end();

         // 初始化消息源
         initMessageSource();

         // 初始化事件多播器
         initApplicationEventMulticaster();

         // 没有具体实现,在上下文子类中可以初始化其他特殊的Bean
         onRefresh();

         // 检查并注册监听器
         registerListeners();

         // 实例化所有剩余的非懒加载的单例Bean
         finishBeanFactoryInitialization(beanFactory);

         // 发布相应的刷新事件
         finishRefresh();
      }

         // 结束刷新上下文
         contextRefresh.end();
   }
}

1.prepareRefresh

该方法主要执行容器刷新前的操作。

  • 设置容器启动时间:this.startupDate = System.currentTimeMillis();
  • 设置关闭状态为false:this.closed.set(false);
  • 设置活跃状态为true:this.active.set(true);
  • 获取environment对象,并将系统的属性值加到environment对象:getEnvironment().validateRequiredProperties();
  • 创建监听器和事件的集合:this.earlyApplicationEvents = new LinkedHashSet<>();
protected void prepareRefresh() {
//...
		this.startupDate = System.currentTimeMillis();
		this.closed.set(false);
		this.active.set(true);
		getEnvironment().validateRequiredProperties();
		this.earlyApplicationEvents = new LinkedHashSet<>();
	}

2.obtainFreshBeanFactory

该方法主要执行以下两个方法

  • refreshBeanFactory:如果当前已经存在Bean工厂,则先销毁已有的Bean并关闭Bean工厂,再解析application.xml配置文件得到bean的定义信息BeanDefinition注册到DefaultListableBeanFactory 的 beanDefinitionMap 中

  • getBeanFactory:返回 DefaultListableBeanFactory

	protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
		refreshBeanFactory();
		return getBeanFactory();
	}
protected final void refreshBeanFactory() throws BeansException {
   // 如果当前已经存在Bean工厂,则先销毁已有的Bean并关闭Bean工厂
   if (hasBeanFactory()) {
      destroyBeans();
      closeBeanFactory();
   }
   try {
      // 创建一个新的DefaultListableBeanFactory实例作为Bean工厂
      DefaultListableBeanFactory beanFactory = createBeanFactory();
      // 设置Bean工厂的序列化ID
      beanFactory.setSerializationId(getId());
      customizeBeanFactory(beanFactory);
      // 将Bean定义信息从配置文件加载到Bean工厂中
      loadBeanDefinitions(beanFactory);
      // 将刚创建的Bean工厂赋值给当前容器的beanFactory属性
      this.beanFactory = beanFactory;
   }
}

关键方法 loadBeanDefinitions(beanFactory);

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
   // 创建XmlBeanDefinitionReader实例,用于读取XML配置文件的Bean定义
   XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

   beanDefinitionReader.setEnvironment(this.getEnvironment());
   beanDefinitionReader.setResourceLoader(this);
   beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
   initBeanDefinitionReader(beanDefinitionReader);
   // 加载Bean定义
   loadBeanDefinitions(beanDefinitionReader);
}

通过loadBeanDefinitions(beanDefinitionReader)去加载beanDefinition

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
   // 获取XML配置文件的数组
   Resource[] configResources = getConfigResources();
   // 如果数组不为空,则去加载配置文件的Bean定义
   if (configResources != null) {
      reader.loadBeanDefinitions(configResources);
   }
   // 获取XML配置文件的路径
   String[] configLocations = getConfigLocations();
   // 如果数组不为空,则加载这些配置文件中的Bean定义
   if (configLocations != null) {
      reader.loadBeanDefinitions(configLocations);
   }
}

继续执行loadBeanDefinitions(configResources);

	public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
		try (InputStream inputStream = encodedResource.getResource().getInputStream()) {
			InputSource inputSource = new InputSource(inputStream);
			if (encodedResource.getEncoding() != null) {
				inputSource.setEncoding(encodedResource.getEncoding());
			}
			//加载配置文件的Bean定义
			return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
		}
	}
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
			throws BeanDefinitionStoreException {

		try {
		// 将XML配置文件封装成doc文档
			Document doc = doLoadDocument(inputSource, resource);
			 // 注册Bean定义,并返回注册的数量
			int count = registerBeanDefinitions(doc, resource);
			if (logger.isDebugEnabled()) {
				logger.debug("Loaded " + count + " bean definitions from " + resource);
			}
			return count;
			}
	}

执行registerBeanDefinitions(doc, resource);

	public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
		int countBefore = getRegistry().getBeanDefinitionCount();
		//使用documentReader注册Bean的定义信息
		documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
		// 返回注册的Bean数量
		return getRegistry().getBeanDefinitionCount() - countBefore;
	}

	public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
		this.readerContext = readerContext;
		//从XML文档的根节点开始注册Bean定义
		doRegisterBeanDefinitions(doc.getDocumentElement());
	}
	
	protected void doRegisterBeanDefinitions(Element root) {
		//...
		// 先对XML文档进行预处理
   preProcessXml(root);
   // 解析XML中的bean定义
   parseBeanDefinitions(root, this.delegate);
   // 解析bean之后,对XML进行后置处理
   postProcessXml(root);

		this.delegate = parent;
	}
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
	//...
	// 解析XML中的bean定义
	parseDefaultElement(ele, delegate);
					
	}
// 根据不同的标签进行解析,有以下四种标签import、alias、bean、beans,重点关注bean
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
   // 如果元素属于import标签
   if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
      // 处理import的元素
      importBeanDefinitionResource(ele);
   }
   // 如果元素属于alias标签
   else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
      // 处理alias的元素
      processAliasRegistration(ele);
   }
   // 如果元素是bean标签
   else if (delegate.nodeName转向ele, BEAN_ELEMENT)) {
      // 处理bean元素
      processBeanDefinition(ele, delegate);
   }
   // 如果元素是nested-beans标签
   else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
      // 递归处理nested-beans元素
      doRegisterBeanDefinitions(ele);
   }
}

重点关注bean标签的处理方法processBeanDefinition(ele, delegate);

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
   // 解析当前元素,得到BeanDefinitionHolder
   BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
   // 如果bdHolder不为空
   if (bdHolder != null) {
      bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
      try {
         // 开始注册BeanDefinition实例
         BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
      }
      getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
   }
}

public static void registerBeanDefinition(
      BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
      throws BeanDefinitionStoreException {

   // 获取bean定义的名称
   String beanName = definitionHolder.getBeanName();
   //在注册表中注册bean定义
   registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

   // 获取bean定义的所有别名
   String[] aliases = definitionHolder.getAliases();
   // 如果存在别名
   if (aliases != null) {
      // 遍历所有别名,并在注册表中为每个别名注册对应的bean名称
      for (String alias : aliases) {
         registry.registerAlias(beanName, alias);
      }
   }
}

真正注册bean的定义方法registerBeanDefinition

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
      throws BeanDefinitionStoreException {

   // 断言beanName不为空
   Assert.hasText(beanName, "Bean name must not be empty");
   // 断言beanDefinition不为空
   Assert.notNull(beanDefinition, "BeanDefinition must not be null");

   // 获取已存在的bean定义
   BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
   //如果已存在bean定义
   if (existingDefinition != null) {
      // Spring不允许覆盖bean定义,会抛出BeanDefinitionOverrideException
      if (!isAllowBeanDefinitionOverriding()) {
         throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
      } 
      // 更新bean定义映射信息
      this.beanDefinitionMap.put(beanName, beanDefinition);
   }
   else {
   
      // 将beanName和beanDefinition存储到beanDefinitionMap中
         this.beanDefinitionMap.put(beanName, beanDefinition);
         this.beanDefinitionNames.add(beanName);
         removeManualSingletonName(beanName);
      }
    

}

总结:首先获取xml 配置文件的路径,通过 documentLoader 将文件封装成 Doc 格式,再通过BeanDefinitionDocumentReader 来解析 xml 文件,从xml文件的根节点开始遍历,根据不同的标签(import、alias、bean、beans)组装成 beanDefinition,再根据 BeanName 查询 beanDefinition 是否已经注册,如果注册过,则直接抛出异常,如果没有注册,则将 BeanName 作为key, beanDefinition作为value 注册到 DefaultListableBeanFactory 的 beanDefinitionMap 中。

3.prepareBeanFactory

该方法主要为DefaultListableBeanFactory类型的beanFactory设置一些默认的属性和方法。

4.postProcessBeanFactory

该方法没有具体实现,不过子类通过该方法对Bean工厂进行处理。

5.invokeBeanFactoryPostProcessors

该方法可以对 beanFactory 的 BeanDefinition 进行修改。

6.registerBeanPostProcessors

该方法会解析Bean创建的Bean前后处理器,前后处理器会在 Bean 初始化方法前后调用,比如下面的例子

public class ExampleBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        // 前置处理
        System.out.println("bean初始化的前置操作");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        // 后置处理
        System.out.println("bean初始化的后置操作");
        return bean;
    }
}

执行结果

bean初始化的前置操作
bean初始化
bean初始化的后置操作

另外,AOP的功能也是在这个方法里实现的。

7.initMessageSource

该方法主要初始化信息源,与实现Spring中国际化的功能有关。

8.initApplicationEventMulticaster

该方法主要初始化容器的事件传播器。

9.onRefresh

该方法默认没有具体实现,在子类中可以初始化其他特殊的Bean。

10.registerListeners

该方法主要为事件传播器注册事件监听器,为广播事件服务。

11.finishBeanFactoryInitialization

该方法是IOC容器中最核心的方法,主要实例化所有剩余的非懒加载的单例Bean,包括实例化,属性填充,初始化等操作。

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
   // 实例化所有的单例bean
   beanFactory.preInstantiateSingletons();
}

public void preInstantiateSingletons(){
    	// 从beanDefinitions获取所有的beanName集合
		List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);		
		// 遍历集合,初始化bean
		for (String beanName : beanNames) {
            // 通过beanName获取bean
            getBean(beanName);
        }
}
public Object getBean(String name) throws BeansException {
    // 获取bean,依赖注入也是在该方法实现的
    return doGetBean(name, null, null, false);
}

protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly){
    // 根据name得到beanName
    String beanName = transformedBeanName(name);
    
    // 检查缓存中是否有单例对象
    Object sharedInstance = getSingleton(beanName);
    
    //如果bean是单例的,会解决循环依赖,如果是原型模式,则会直接抛出异常
    if (isPrototypeCurrentlyInCreation(beanName)) {
        throw new BeanCurrentlyInCreationException(beanName);
    }
    
    if (mbd.isSingleton()) {
        // 得到beanName的对应的单例对象
        sharedInstance = getSingleton(beanName, () -> {
            try {
                //创建bean实例 
                return createBean(beanName, mbd, args);
            }
        });
}
          
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args){
    //创建bean实例
    Object beanInstance = doCreateBean(beanName, mbdToUse, args);
}
    
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args){
    // 实例化bean
    BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);
    
    // 填充bean的属性
    populateBean(beanName, mbd, instanceWrapper);
    
    // 执行初始化bean的方法
    exposedObject = initializeBean(beanName, exposedObject, mbd);
}

总结:创建bean实例主要有以下步骤,首先遍历beanDefinitionNames得到每一个beanName,根据BeanName到单例缓存中查找是否已经存在实例,如果存在直接返回,如果不存在,则根据beanName的 beanDefinition 生成bean实例, 实例的生成分为三个步骤:

  1. createBeanInstance 创建实例。
  2. populateBean 进行属性填充。
  3. initializeBean 进行初始化操作:先执行 BeanPostProcessor 的前置方法,再执行bean对象的初始化方法,最后执行 BeanPostProcessor 的后置方法。
11.1createBeanInstance

首先是创建实例的方法

BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {
    // 根据beanName得到Class
    Class<?> beanClass = resolveBeanClass(mbd, beanName);
    
    // 通过无参构造创建实例
    return instantiateBean(beanName, mbd);
}

protected BeanWrapper instantiateBean(String beanName, RootBeanDefinition mbd) {
    // 实例化操作
    beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, this);
}

public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
    // 加锁保证线程安全
    synchronized (bd.constructorArgumentLock) {
        // 得到bean的Class
        Class<?> clazz = bd.getBeanClass();
        // 通过class得到构造方法
        constructorToUse = clazz.getDeclaredConstructor();
        // 反射方式创建实例 
        return BeanUtils.instantiateClass(constructorToUse);
    }
}

public static <T> T instantiateClass(Constructor<T> ctor, Object... args) {
    // 通过反射的方式创建实例
    return ctor.newInstance(argsWithDefaultValues);
}

11.2populateBean

属性填充就是将property标签的value注入给对象的属性name。

<bean id="messageService" class="com.mashibing.hls.MessageServiceImpl">
    <property name="example" value="value"/>
</bean>
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
    // 得到BeanDefinition的属性value
    PropertyValues pvs = mbd.getPropertyValues();
    if (pvs != null) {
        // 将value注入给属性name
        applyPropertyValues(beanName, mbd, bw, pvs);
    }
}

protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs){
    // 获取PropertyValue对象数组存储到列表
    List<PropertyValue> original = Arrays.asList(pvs.getPropertyValues());    
    for (PropertyValue pv : original) {
        // 获取属性的name
        String propertyName = pv.getName();
        // 获取value
        Object originalValue = pv.getValue();      
        // 设置属性值
        bw.setPropertyValues(new MutablePropertyValues(deepCopy));
    }
}

public void setPropertyValues(PropertyValues pvs){
    setPropertyValues(pvs, false, false);
}

public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid){
    // 通过反射的方式进行赋值
    setPropertyValue(pv);
}

11.3initializeBean

初始化的过程就是在执行 BeanPostProcessor 的前后置方法过程中穿插执行init方法。

// 初始化方法
exposedObject = initializeBean(beanName, exposedObject, mbd);

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
		//调用postProcessBeforeInitialization方法,返回原始Bean包装器
    wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);

	//调用初始化方法
	invokeInitMethods(beanName, wrappedBean, mbd);
	
	// 调用postProcessAfterInitialization方法,再返回Bean包装器
	wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
		
	//返回包装后的Bean
	return wrappedBean;
}

// 执行BeanPostProcessors接口下的类
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName){
       Object result = existingBean;
		//遍历BeanPostProcessors列表
		for (BeanPostProcessor processor : getBeanPostProcessors()) {
			// 返回给定的Bean
			Object current = processor.postProcessBeforeInitialization(result, beanName);
			// 如果 current为null
			if (current == null) {
				//直接返回result
				return result;
			}
			result = current;
		}
		//返回经过后置处理后的result
		return result;
}

protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd){
       // 如果mbd不为空且bean不是NullBean
		if (mbd != null && bean.getClass() != NullBean.class) {
			// 获取初始化方法名
			String initMethodName = mbd.getInitMethodName();
			// 调用自定义的init方法
			invokeCustomInitMethod(beanName, bean, mbd);
            
          // 反射执行方法
      Method methodToInvoke = ClassUtils.getInterfaceMethodIfPossible(initMethod);
		methodToInvoke.invoke(bean);
		}
}

// 执行所有的BeanPostProcessors接口下的类
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName){

		//初始化结果对象为result,默认引用existingBean
		Object result = existingBean;
		//遍历BeanPostProcessors列表
		for (BeanPostProcessor processor : getBeanPostProcessors()) {
			//postProcessAfterInitialization对bean实例进行包装
			Object current = processor.postProcessAfterInitialization(result, beanName);
			// 如果current为null
			if (current == null) {
				//直接返回result
				return result;
			}

			result = current;
		}
		//返回包装后的result
		return result;
	}

12.finishRefresh

到这所有的对象已经创建完成,容器已经完成启动,该方法会发布容器的生命周期事件。

13.销毁阶段

创建完对象就可以正常使用,使用完成后就进入销毁阶段,Spring会调用close方法销毁

public void close() {
    synchronized (this.startupShutdownMonitor) {
    //销毁bean
        doClose();
    }
}

protected void doClose() {
    // 清空BeanFactory的缓存
    destroyBeans();
    // 再将beanFactory置为null
    closeBeanFactory();
}

protected void destroyBeans() {
    // 清除beanName与Bean包含的Bean名称集的映射集合
    this.containedBeanMap.clear();
    // 清除beanName与一组相关的Bean名称的映射集合
    this.dependentBeanMap.clear();
    // 清除beanName与bean依赖项的映射集合
    this.dependenciesForBeanMap.clear();
    // 清除注册表中所有的单例实例
    clearSingletonCache();
}

protected void clearSingletonCache() {
    // 使用singletonObjects对象进行同步,确保只有一个线程可以执行清除方法
    synchronized (this.singletonObjects) {       
        // 清空singletonObjects集合,该集合存储了所有的单例Bean实例
        this.singletonObjects.clear();     
        // 清空singletonFactories集合,该集合存储了所有的单例Bean工厂
        this.singletonFactories.clear();    
        // 清空earlySingletonObjects集合,该集合存储了早期暴露的单例Bean实例
        this.earlySingletonObjects.clear();    
        // 清空registeredSingletons集合,该集合存储了所有已注册的单例Bean名称
        this.registeredSingletons.clear(); 
        // 将标志设置为false,表示当前没有单例Bean正在销毁
        this.singletonsCurrentlyInDestruction = false;
    }
}

private void clearByTypeCache() {
   // 清空按类型缓存的Bean名称集合
    this.allBeanNamesByType.clear();
    // 清空按类型缓存的单例Bean名称的集合
    this.singletonBeanNamesByType.clear();
}

protected final void closeBeanFactory() {
    DefaultListableBeanFactory beanFactory = this.beanFactory;
    if (beanFactory != null) {
        beanFactory.setSerializationId(null);
        // 将beanFactory设置为空
        this.beanFactory = null;
    }
}

另外可以在配置文件中自定义初始化 init 和销毁 destory 的方法

public class CourseServiceImpl implements CourseService  {
    @Override
    public String getCourseName() {
        return "Spring IOC源码";
    }

    public void init(){
        System.out.println("类的初始化阶段");
    }

    public void destroy(){
        System.out.println("类的销毁阶段");
    }

}

然后在配置文件添加对应的标签即可。

<bean id="courseService" class="com.czl.spring.CourseServiceImpl" init-method="init" destroy-method="destroy"/>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2198610.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Day05-数据库服务索引应用

Day-05-数据库索引知识说明 1、数据库索引概述介绍2、数据库索引分类介绍3、数据库索引应用配置4、数据库执行计划概述5、数据库执行计划获取7、数据库索引覆盖长度8、数据库联合索引应用 01.数据库索引概述介绍 02.数据库索引分类介绍 03.数据库索引应用配置 04.数据库执行计划…

C高级(Day25)

一、学习内容 Shell脚本 shell中的变量 shell中变量的定义 shell中的变量是没有数据类型的&#xff0c;&#xff0c;默认是字符串 shell中的变量默认是全局变量 格式 变量名 错误&#xff0c;计算机认为变量名是一个指令 变量名 正确&#xff0c;定义变量 &#xff0c;值为空…

基于Springboot+Vue的公寓出租系统 (含源码数据库)

1.开发环境 开发系统:Windows10/11 架构模式:MVC/前后端分离 JDK版本: Java JDK1.8 开发工具:IDEA 数据库版本: mysql5.7或8.0 数据库可视化工具: navicat 服务器: SpringBoot自带 apache tomcat 主要技术: Java,Springboot,mybatis,mysql,vue 2.视频演示地址 3.功能 这个系…

成都睿明智科技有限公司靠谱吗怎么样?

在这个短视频与直播带货风起云涌的时代&#xff0c;抖音电商凭借其庞大的用户基数和高效的流量转化能力&#xff0c;成为了众多商家争相入驻的新蓝海。而在这场电商盛宴中&#xff0c;专业的服务商如成都睿明智科技有限公司&#xff0c;更是成为了连接品牌与消费者的桥梁。那么…

Win10 安装 Redis 数据库

一、Redis 数据库介绍 Redis 是一个开源的高性能键值对&#xff08;key-value&#xff09;的非关系型数据库。它通常用作数据结构服务器&#xff0c;支持多种类型的数据结构&#xff0c;如字符串&#xff08;strings&#xff09;、哈希&#xff08;hashes&#xff09;、列表&a…

同城O2O系统源码与跑腿配送平台的架构设计与开发方案详解

今天&#xff0c;笔者将与您一同深入探讨同城O2O系统的源码及跑腿配送平台的架构设计与开发方案&#xff0c;助力开发者和企业在这一领域的实践与探索。 一、O2O系统概述 在同城O2O模式中&#xff0c;用户可以通过手机应用或网页平台下单&#xff0c;而配送员则根据订单信息迅…

redis高级(面试题一)

目录 一、Redis持久化机制有哪些&#xff1f;有什么区别&#xff1f; 1、RDB(redis database) 2、AOF 3、区别 二、redis的主从集群有什么用&#xff1f; 1、什么是redis的主从集群 2、为什么要使用redis主从集群&#xff08;也就是有什么用&#xff09; 三、redis的哨兵…

ORA-19815 db_recovery_file_dest_size 100%

1、alert日志报错 ORA-19815 db_recovery_file_dest_size 100% 恢复区空间使用满 2、rm删除后操作系统空间使用&#xff0c;但V$RECOVERY_FILE_DEST记录的空间使用率仍然是满的 3、rman delete expired 归档日志后恢复正常 4、当然可以通过增大db_recovery_file_dest_size来临时…

算法笔记(十四)——多源 BFS

文章目录 <font colorred>01 矩阵<font colorred>飞地的数量/font>地图中的最高点地图分析 多源 BFS 单源最短路问题&#xff1a;一个起点到一个终点的最短路&#xff1b; 解决步骤&#xff1a; 把起点放进队列里一层一层往外扩 相关文章&#xff1a;算法笔记&a…

0基础跟德姆(dom)一起学AI 机器学习04-逻辑回归

逻辑回归简介 应用场景 逻辑回归是解决二分类问题的利器 数学知识 sigmoid函数 概率 极大似然估计 核心思想&#xff1a; 设模型中含有待估参数w&#xff0c;可以取很多值。已经知道了样本观测值&#xff0c;从w的一切可能值中&#xff08;选出一个使该观察值出现的概率为…

C++拾趣——绘制Console中DropdownMenu

大纲 居中显示窗口清屏并重设光标绘制窗口绘制窗口顶部绘制下拉行绘制下拉框选项绘制按钮行绘制窗口底部 修改终端默认行为对方向键的特殊处理过程控制Tab键的处理Enter键的处理上下左右方向键的处理 完整代码代码地址 这次我们要绘制下拉菜单&#xff0c;如下图。 居中显示窗口…

C语言 | Leetcode C语言题解之第466题统计重复个数

题目&#xff1a; 题解&#xff1a; #include <stdlib.h> #include <stdio.h> #include <stdbool.h> #include <string.h> #include <math.h> #include <limits.h>#define MMAX(a, b) ((a) > (b)? (a) : (b)) #define MMIN(a,…

xianshan分支预测器BPU

xianshan分支预测器BPU 1 RISC-V分支预测1.0 分支预测基本类型1.0.1 条件分支指令1.0.2 无条件分支指令 1.1 方向预测1.1.1 饱和计数器法1.1.1.1 Questions 1.1.2 分支历史法--程序局部性原理1.1.2.1 Questions 1.2 目标地址预测1.2.1 分支目标缓存--BTB 1.3 预测指令类型1.3.1…

【小白向】机器人入门之ROS系统的学习(Ubuntu24.04+ROS2)

目录 一.复杂的机器人系统 二.ROS机器人系统 1.简介 1.节点 2.话题 2.安装 3.测试 4.可视化 RQT&#xff1a; RVIZ&#xff1a; 显示属性&#xff1a; 显示状态&#xff1a; 一.复杂的机器人系统 依照我们现在的技术来看&#xff0c;机器人系统仍是极其复杂的&#xff0c;往…

深入了解音频剪辑在线工具的特色与优势

在数字时代&#xff0c;音频内容已成为连接人心的重要桥梁。如果你也有同样的兴趣爱好&#xff0c;那不妨看看我今天要介绍的音频剪辑在线相关的工具们吧。 1.福昕音频剪辑 链接直达>>https://www.foxitsoftware.cn/audio-clip/ 福昕音频剪辑工具&#xff0c;专为音乐…

【H2O2|全栈】关于CSS(11)flex——更加优雅的布局

目录 CSS3入门 前言 准备工作 布局优化 如何使用flex布局 容器与成员 概念 轴线 容器的属性 成员的属性 预告和回顾 后话 CSS3入门 前言 本系列博客主要介绍CSS有关知识点&#xff0c;当前章节讲述CSS3相关内容。 本章节讲述flex布局的相关知识。 部分内容仅代…

数据结构 ——— 单链表oj题:环形链表

目录 题目要求 手搓简易环状单链表 代码实现 问题1&#xff1a;slow 指针和 fast 指针一定会相遇吗 问题2&#xff1a;slow 每次走一步&#xff0c;fast 每次走 n 步是否还能判断链表带环&#xff1f;&#xff08;n > 2&#xff09; 题目要求 有一个单链表的头节点 …

【翻译】在 Python 应用程序中使用Qt Designer的UI文件

原文地址&#xff1a;Using a Designer UI File in Your Qt for Python Application 直接上图&#xff0c;上代码 将UI文件转为Python To demonstrate, we use the Qt Widgets animation easing example. 为了演示&#xff0c;我们使用 Qt Widgets 动画简化示例。 这个应用程…

快递查询软件:实现单号识别与批量物流查询的高效工具

随着网络购物的普及&#xff0c;快递物流行业迎来了前所未有的发展机遇&#xff0c;同时也面临着巨大的挑战。跟踪物流信息成为一个难题&#xff0c;因此&#xff0c;快递查询软件的核心功能之一便是单号识别。传统的快递单号输入方式繁琐且易出错在此背景下&#xff0c;快递查…

游戏盒子推广全攻略:从用户洞察到策略实施

在移动互联网时代&#xff0c;游戏盒子的推广已经成为众多游戏代理商和开发者的重要课题。面对激烈的市场竞争&#xff0c;如何高效吸引并留住玩家&#xff0c;成为游戏盒子推广的关键。本文将结合Xinstall这一专业App推广工具&#xff0c;探讨游戏盒子推广的有效策略。 一、市…