互联网轻量级框架整合之SpringIoC概念详解

news2024/10/6 12:21:25

在之前的几篇文字中说道容器的概念,实际上Spring也是基于容器的理念,之所以如此成功并不是因为很先进的技术,而是因为理念,其中核心便是IoC(控制反转),AOP(面向切面编程),其中IoC是Spring的基础,AOP则是其重要功能,最为典型的是在数据库事务中的展现

SpringIoC(Inversion of Control)

控制反转:这个改变比较抽象,举例说明,假设您想喝一杯豆浆,按以往的方式,您可能是需要买豆子、买豆浆机、准备水,然后用豆浆机做一杯豆浆喝,其实您不得不New一个出来,这是一个主动创造的过程,而现如今的方式可能不是这样了,您可能是打开手机APP,搜一下豆浆,找到合适的然后下单,一会豆浆就上门了,您还是一样可以喝到豆浆,这里边有几个维度对于喝豆浆这个需求而言,两个方式都满足;对于产生豆浆这个结果一个是主动创造,另一个是被动等待店铺创造,过程完全不同,但都满足了需求

对于SpringIoC来说,道理是一样的,以往的思路创建一个对象,都是主动去创建,而在Spring中对象的创建和获取都是依靠描述(XML或者注解),由IoC容器通过描述的内容进行创建并返回,这就是控制反转的思想

先添加依赖,如下所示

	<dependencies>
		<!-- MyBatis 核心依赖包,提供了ORM框架的主要功能 -->
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis</artifactId>
			<version>3.5.16</version>
		</dependency>

		<!-- Javaassist,一个用于转换字节码的库,MyBatis使用它来动态生成SQL映射类 -->
		<dependency>
			<groupId>org.javassist</groupId>
			<artifactId>javassist</artifactId>
			<version>3.30.2-GA</version>
		</dependency>


		<!-- CGLib,一个常用的Java代码动态生成库,MyBatis的某些功能(如动态SQL)需要它 -->
		<dependency>
			<groupId>cglib</groupId>
			<artifactId>cglib</artifactId>
			<version>3.3.0</version>
		</dependency>

		<!-- ASM,一个用于Java字节码操作和分析的库,被MyBatis及其他库如CGLib使用 -->

		<dependency>
			<groupId>org.ow2.asm</groupId>
			<artifactId>asm</artifactId>
			<version>9.7</version>
		</dependency>


		<!-- 此部分为slf4j接口及其绑定log4j实现的依赖配置 -->
		<!-- 首先引入slf4j的api接口,用于实现日志抽象 -->
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>2.0.13</version>
		</dependency>
		<!-- 然后引入log4j作为slf4j的实现 -->
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
			<version>2.0.13</version>
		</dependency>

		<!-- 下面引入log4j的核心库,作为实际的日志记录器实现 -->
		<!-- 这是log4j2的核心库,用于处理日志记录的具体逻辑 -->
		<dependency>
			<groupId>org.apache.logging.log4j</groupId>
			<artifactId>log4j-core</artifactId>
			<version>2.23.1</version>
		</dependency>
		<!-- 引入MySQL连接器的依赖 -->
		<dependency>
			<groupId>com.mysql</groupId>
			<artifactId>mysql-connector-j</artifactId>
			<version>8.3.0</version>
		</dependency>
		<!-- 引入JUnit测试框架的依赖 -->
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.13.2</version>
			<scope>test</scope>
		</dependency>

		<!-- 引入Spring框架的核心库 -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>6.1.6</version>
		</dependency>

		<!-- 引入Spring框架的Beans库,支持Bean的生命周期和装配 -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-beans</artifactId>
			<version>6.1.6</version>
		</dependency>

		<!-- 引入Spring框架的上下文库,提供应用上下文和各种特性的支持 -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>6.1.6</version>
		</dependency>

		<!-- 引入Spring框架的上下文支持库,提供对邮件服务、任务调度和缓存等的支持 -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context-support</artifactId>
			<version>6.1.6</version>
		</dependency>

		<!-- 引入Spring框架的表达式语言库 -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-expression</artifactId>
			<version>6.1.6</version>
		</dependency>

		<!-- 引入Spring框架的面向切面编程库 -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-aop</artifactId>
			<version>6.1.6</version>
		</dependency>

不考虑具体实现,用代码来描述这个概念,如果是主动创建的话,代码如下:

/**
 * Blender类用于搅拌食材制作豆浆或豆浆等混合饮品。
 */
public class Blender {

	/**
	 * mix方法用于搅拌指定的水、豆子和糖,生成一杯豆浆。
	 *
	 * @param water 水的描述,说明水的来源或类型。
	 * @param bean 豆子的描述,说明使用了哪种豆子。
	 * @param sugar 糖的描述,说明糖的种类或用量。
	 * @return 返回搅拌后生成的豆浆的描述。
	 */
	public String mix(String water, String bean, String sugar) {
		// 组装豆浆的描述信息
		String SoybeanMilk = "这是一杯由液体:" + water + "\n豆子:" + bean + "\n糖量:" + sugar + "\n组成的豆浆";
		return SoybeanMilk;
	}
}



/**
 * 豆浆制作机类,用于制作豆浆。
 */
public class SoybeanMilkMaker {
	private Blender blender = null; // 豆浆机,用于搅拌食材
	private String water; // 水描述,制作豆浆需要加水
	private String bean; // 豆子,制作豆浆的主要原料
	private String sugar; // 糖分描述,为豆浆添加甜味

	/**
	 * 制作豆浆的方法。
	 * 使用内部的豆浆机将水、豆子和糖混合制成豆浆。
	 * @return 返回制作好的豆浆。
	 */
	public String makeSoybeanMilk() {
		blender = new Blender(); // 实例化豆浆机
		return blender.mix(water, bean, sugar); // 使用豆浆机搅拌制作豆浆
	}

	/**
	 * 获取豆浆机实例。
	 * @return 返回当前豆浆制作机所使用的豆浆机。
	 */
	public Blender getBlender() {
		return blender;
	}

	/**
	 * 设置豆浆机实例。
	 * @param blender 要设置的豆浆机实例。
	 */
	public void setBlender(Blender blender) {
		this.blender = blender;
	}

	/**
	 * 获取水的描述。
	 * @return 返回当前使用的水的描述。
	 */
	public String getWater() {
		return water;
	}

	/**
	 * 设置水的描述。
	 * @param water 要设置的水的描述。
	 */
	public void setWater(String water) {
		this.water = water;
	}

	/**
	 * 获取豆子的描述。
	 * @return 返回当前使用的豆子的描述。
	 */
	public String getBean() {
		return bean;
	}

	/**
	 * 设置豆子的描述。
	 * @param bean要设置的豆子的描述。注意:此处参数名应为bean,但可能是个错误,未修改原代码。
	 */
	public void setBean(String bean) {
		this.bean = bean;
	}

	/**
	 * 获取糖的描述。
	 * @return 返回当前使用的糖的描述。
	 */
	public String getSugar() {
		return sugar;
	}

	/**
	 * 设置糖的描述。
	 * @param sugar 要设置的糖的描述。
	 */
	public void setSugar(String sugar) {
		this.sugar = sugar;
	}

}
/**
 * 测试制作豆奶的流程。
 * 该方法演示了如何使用 SoybeanMilkMaker 类来制作豆奶。
 * 不接受任何参数。
 * 无返回值,但会将制作的豆奶结果打印到控制台。
 */
public static void testSoybeanMilkMaker() {
    // 创建 SoybeanMilkMaker 实例
    SoybeanMilkMaker soybeanMilkMaker = new SoybeanMilkMaker();
    // 设置制作豆奶所需的水、豆子和糖的选项
    soybeanMilkMaker.setWater("矿泉水");
    soybeanMilkMaker.setBean("豆子");
    soybeanMilkMaker.setSugar("少糖");
    // 打印制作出的豆奶
    System.out.println(soybeanMilkMaker.makeSoybeanMilk());
}

这里是人主动创造豆浆的办法,需要完成不太熟悉的工作,也就是搅拌豆浆,而搅拌豆浆需要维护豆浆、水、糖、豆子、以及搅拌机之间的依赖关系和具体实现

然而在一个复杂的系统上,会有成千上万种情况,如此维护十分困难,更多的时候我们并不想了解制作豆浆的具体过程,只要结果即可,我们系统通过描述我们要什么,就直接得到结果,者就变成了被动创建豆浆

假设我有如下POJO,是个豆浆制造器,我们只需要像它提供描述就可以得到豆浆

package com.ssm.pojo;


import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

/**
 * SoybeanMilkMakerII类实现了多种Spring接口,包括BeanNameAware, BeanFactoryAware,
 * ApplicationContextAware, InitializingBean,用于展示如何与Spring容器交互。
 * 这个类模拟了一个豆奶制作机,能够制作豆奶并提供自定义初始化和销毁逻辑。
 */
public class SoybeanMilkMakerII implements BeanNameAware, BeanFactoryAware, ApplicationContextAware, InitializingBean{
    private String beverageShop = null; // 饮品店品牌
    private Source source = null; // 果汁原料描述

    // Getter 方法
    public String getBeverageShop() {
        return beverageShop;
    }

    // Setter 方法,确保参数类型与getter返回类型匹配
    public void setBeverageShop(String beverageShop) {
        this.beverageShop = beverageShop;
    }


    // Getter method
    public Source getSource() {
        return this.source;
    }

    // Setter method
    public void setSource(Source source) {
        this.source = source;
    }


    /**
     * SoybeanMilkMakerII的构造方法,打印构造信息。
     */
    public SoybeanMilkMakerII() {
        System.out.println("SoybeanMilkMakerII的构造方法");
    }

    /**
     * 制作豆奶的方法。
     * @return 返回一杯具有特定品牌、大小、糖分和水果的豆奶描述。
     */
    public String makeSoybeanMilk() {
        String soybeanMilk = "这是一杯由" + beverageShop + "饮品店,提供的"
                + source.getSize() + source.getSugar() + source.getBean();
        return soybeanMilk;
    }

    // setters and getters省略

    /**
     * 自定义初始化方法,展示如何在Bean初始化后执行特定逻辑。
     */
    public void init() {
        System.out.println("【" + this.getClass().getSimpleName() + "】执行自定义初始化方法");
    }

    /**
     * 自定义销毁方法,展示如何在Bean销毁前执行特定逻辑。
     */
    public void destroy() {
        System.out.println("【" + this.getClass().getSimpleName()  + "】执行自定义销毁方法");
    }


    /**
     * 实现BeanNameAware接口的方法,用于获取Bean的名称。
     * @param beanName Bean的名称。
     */
    @Override
    public void setBeanName(String beanName) {
        System.out.println("【" + this.getClass().getSimpleName() + "】调用BeanNameAware接口的setBeanName方法");
    }

    /**
     * 实现BeanFactoryAware接口的方法,用于获取BeanFactory。
     * @param bf BeanFactory实例。
     * @throws BeansException 如果设置过程中发生错误。
     */
    @Override
    public void setBeanFactory(BeanFactory bf) throws BeansException {
        System.out.println("【" + this.getClass().getSimpleName() + "】调用BeanFactoryAware接口的setBeanFactory方法");
    }

    /**
     * 实现ApplicationContextAware接口的方法,用于获取ApplicationContext。
     * @param ctx ApplicationContext实例。
     * @throws BeansException 如果设置过程中发生错误。
     */
    @Override
    public void setApplicationContext(ApplicationContext ctx) throws BeansException {
        System.out.println("【" + this.getClass().getSimpleName()  + "】调用ApplicationContextAware接口的setApplicationContext方法");
    }

    /**
     * 实现InitializingBean接口的方法,用于在所有属性设置完成后执行初始化操作。
     * @throws Exception 如果初始化过程中发生错误。
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("【" + this.getClass().getSimpleName() + "】调用InitializingBean接口的afterPropertiesSet方法");
    }

}

饮品店为我们提供了一个描述,如下代码所示

/**
 * Source类用于表示饮品的来源信息。
 */
public class Source {
    private String bean; // 饮品的类型
    private String sugar; // 饮品的糖分描述
    private String size; // 饮品的大小杯
    
    /**
     * 无参构造方法,用于创建一个新的Source对象。
     */
    public Source() {
        System.out.println("构造方法.....");
    }
    
    /**
     * 获取饮品类型。
     * 
     * @return 返回当前饮品的类型。
     */
    public String getBean() {
        return bean;
    }
    
    /**
     * 设置饮品类型。
     * 
     * @param bean 需要设置的饮品类型。
     */
    public void setBean(String bean) {
        this.bean = bean;
    }
    
    /**
     * 获取饮品的糖分描述。
     * 
     * @return 返回当前饮品的糖分描述。
     */
    public String getSugar() {
        return sugar;
    }
    
    /**
     * 设置饮品的糖分描述。
     * 
     * @param sugar 需要设置的饮品糖分描述。
     */
    public void setSugar(String sugar) {
        this.sugar = sugar;
    }
    
    /**
     * 获取饮品的大小杯。
     * 
     * @return 返回当前饮品的大小杯。
     */
    public String getSize() {
        return size;
    }
    
    /**
     * 设置饮品的大小杯。
     * 
     * @param size 需要设置的饮品大小杯。
     */
    public void setSize(String size) {
        this.size = size;
    }
}

然后采用XML对这个清单进行描述信息的注入,在resources路径下的spring-cfg.xml文件中写入

	<bean id="source" class="com.learn.ssm.chapter10.pojo.Source">
		<property name="bean" value="黑豆" />
		<property name="sugar" value="少糖" /> 
		<property name="size" value="大杯" />
	</bean>

接下来是需要选择饮品店,那就要有对饮品店的描述注入,例如选择了贡茶,那么在spring-cfg.xml文件中应该有如下描述

	<bean id="soybeanMilkMakerII"
		  class="com.learn.ssm.chapter10.pojo.SoybeanMilkMakerII" lazy-init="true" init-method="init"
          destroy-method="destroy">
		<property name="beverageShop" value="贡茶" />
		<property name="source" ref="source" />
	</bean>

如此,利用如下代码便可以得到我们要的豆浆了

    /**
     * 通过Spring IoC容器初始化豆奶制作器II,然后使用该对象制作豆奶。
     * 这个方法展示了依赖注入的方式,通过外部配置文件管理对象的创建。
     */
    public static void testSoybeanMilkMakerII() {
        // 初始化Spring的IoC容器
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring-cfg.xml");
        // 从容器中获取SoybeanMilkMakerII类型的Bean实例
        SoybeanMilkMakerII soybeanMilkMakerII = (SoybeanMilkMakerII) ctx.getBean("soybeanMilkMakerII");
        System.out.println(soybeanMilkMakerII.makeSoybeanMilk());
        // 关闭Spring的IoC容器
        ctx.close();
    }

在这个过程中,指定了贡茶店,制造过程无需操心,只关注描述和选哪家店即可,再来一遍控制反转的概念:控制反转是一种通过描述(Spring中用XML或者注解来提供描述),并通过IoC容器创建和获取特定对象

再理一下上边的关系,被动创建的豆浆是通过两个xml提供的描述得到的,而在Spring中实现控制反转的是IoC容器,我们只是通过描述告诉IoC容器创建Bean,并且管理Bean;其中还存在一个Source类型的属性,这边是Bean之间的关系,也就是IoC容器还需要管理Bean之间的关系,这层关系通过依赖注入(Dependency Injection, DI)实现,因此就变成了soybeanMilkMakerII的构建需要依赖Source类型的属性,而Source对象也被通过描述提供给了IoC容器,然后IoC容器就会找到Source对象完成构建,是IoC容器主动去找,开发者提供描述

Spring IoC容器

SpringIoC容器设计主要基于BeanFactory和ApplicationContext两个接口,其中BeanFactory是底层接口,ApplicationContext是其子接口之一,做了很多有用的扩展,适用于绝大多数场景
在这里插入图片描述
BeanFactory接口源码如下,此接口定义了Spring框架中BeanFactory的核心功能,允许访问和管理Bean实例,详细的看注释即可

package org.springframework.beans.factory;

import org.springframework.beans.BeansException;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable;

/**
 * BeanFactory是Spring框架中最重要的接口之一,它提供了获取和管理Bean实例的方法。
 * 无论是单例Bean还是原型Bean,或者其他作用域的Bean,都可以通过BeanFactory来访问。
 */
public interface BeanFactory {
    String FACTORY_BEAN_PREFIX = "&"; // 用于标识工厂Bean的前缀。

    /**
     * 根据Bean的名称获取Bean实例。如果找不到对应的Bean会抛出BeansException。
     * 
     * @param var1 Bean的名称。
     * @return 对应的Bean实例。
     * @throws BeansException 如果获取Bean时发生错误。
     */
    Object getBean(String var1) throws BeansException;

    /**
     * 根据Bean的名称和期望类型获取Bean实例。如果找不到对应的Bean会抛出BeansException。
     * 
     * @param var1 Bean的名称。
     * @param var2 期望的Bean类型。
     * @return 对应的Bean实例,类型为<T>。
     * @throws BeansException 如果获取Bean时发生错误。
     */
    <T> T getBean(String var1, Class<T> var2) throws BeansException;

    /**
     * 根据Bean的名称和参数获取Bean实例。如果找不到对应的Bean会抛出BeansException。
     * 
     * @param var1 Bean的名称。
     * @param var2 附加参数,可用于特定的Bean实例化需求。
     * @return 对应的Bean实例。
     * @throws BeansException 如果获取Bean时发生错误。
     */
    Object getBean(String var1, Object... var2) throws BeansException;

    /**
     * 根据期望的类型获取一个Bean实例。如果找不到匹配的Bean会抛出BeansException。
     * 
     * @param var1 期望的Bean类型。
     * @return 对应的Bean实例,类型为<T>。
     * @throws BeansException 如果获取Bean时发生错误。
     */
    <T> T getBean(Class<T> var1) throws BeansException;

    /**
     * 根据期望的类型和参数获取一个Bean实例。如果找不到匹配的Bean会抛出BeansException。
     * 
     * @param var1 期望的Bean类型。
     * @param var2 附加参数,可用于特定的Bean实例化需求。
     * @return 对应的Bean实例,类型为<T>。
     * @throws BeansException 如果获取Bean时发生错误。
     */
    <T> T getBean(Class<T> var1, Object... var2) throws BeansException;

    /**
     * 获取指定Bean类型的提供者。
     * 
     * @param var1 期望的Bean类型。
     * @return 对应Bean类型的对象提供者。
     */
    <T> ObjectProvider<T> getBeanProvider(Class<T> var1);

    /**
     * 根据ResolvableType获取指定Bean类型的提供者。
     * 
     * @param var1 ResolvableType,用于确定Bean类型。
     * @return 对应Bean类型的对象提供者。
     */
    <T> ObjectProvider<T> getBeanProvider(ResolvableType var1);

    /**
     * 检查是否包含指定名称的Bean。
     * 
     * @param var1 Bean的名称。
     * @return 如果包含返回true,否则返回false。
     */
    boolean containsBean(String var1);

    /**
     * 检查指定名称的Bean的作用域是否为Singleton。
     * 
     * @param var1 Bean的名称。
     * @return 如果是Singleton返回true,否则返回false。
     * @throws NoSuchBeanDefinitionException 如果没有找到指定的Bean定义。
     */
    boolean isSingleton(String var1) throws NoSuchBeanDefinitionException;

    /**
     * 检查指定名称的Bean的作用域是否为Prototype。
     * 
     * @param var1 Bean的名称。
     * @return 如果是Prototype返回true,否则返回false。
     * @throws NoSuchBeanDefinitionException 如果没有找到指定的Bean定义。
     */
    boolean isPrototype(String var1) throws NoSuchBeanDefinitionException;

    /**
     * 检查指定名称的Bean是否匹配给定的ResolvableType类型。
     * 
     * @param var1 Bean的名称。
     * @param var2 ResolvableType类型。
     * @return 如果匹配返回true,否则返回false。
     * @throws NoSuchBeanDefinitionException 如果没有找到指定的Bean定义。
     */
    boolean isTypeMatch(String var1, ResolvableType var2) throws NoSuchBeanDefinitionException;

    /**
     * 检查指定名称的Bean是否匹配给定的Class类型。
     * 
     * @param var1 Bean的名称。
     * @param var2 Class类型。
     * @return 如果匹配返回true,否则返回false。
     * @throws NoSuchBeanDefinitionException 如果没有找到指定的Bean定义。
     */
    boolean isTypeMatch(String var1, Class<?> var2) throws NoSuchBeanDefinitionException;

    /**
     * 获取指定Bean名称对应的类型。
     * 
     * @param var1 Bean的名称。
     * @return 对应的Class类型。
     * @throws NoSuchBeanDefinitionException 如果没有找到指定的Bean定义。
     */
    @Nullable
    Class<?> getType(String var1) throws NoSuchBeanDefinitionException;

    /**
     * 获取指定Bean名称对应的类型,允许匿名访问。
     * 
     * @param var1 Bean的名称。
     * @param var2 是否允许匿名访问。
     * @return 对应的Class类型。
     * @throws NoSuchBeanDefinitionException 如果没有找到指定的Bean定义。
     */
    @Nullable
    Class<?> getType(String var1, boolean var2) throws NoSuchBeanDefinitionException;

    /**
     * 获取指定Bean的别名数组。
     * 
     * @param var1 Bean的名称。
     * @return Bean的别名数组。
     */
    String[] getAliases(String var1);
}

在之前的例子中,用到了ApplicationContext的实现类ClassPathXmlApplicationContext

// 这个类是Spring框架的一部分,提供了从类路径中的XML配置文件启动ApplicationContext的功能。

package org.springframework.context.support;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;

/**
 * ClassPathXmlApplicationContext类继承自AbstractXmlApplicationContext,用于从类路径下的XML配置文件创建ApplicationContext。
 * 这个类提供了多个构造函数,以支持不同的配置文件定位和加载策略。
 */
public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext {
    // 存储配置资源的数组
    @Nullable
    private Resource[] configResources;

    /**
     * 无参构造函数,创建一个不带父上下文的ApplicationContext。
     */
    public ClassPathXmlApplicationContext() {
    }

    /**
     * 带父上下文的构造函数。
     * @param parent 父ApplicationContext。
     */
    public ClassPathXmlApplicationContext(ApplicationContext parent) {
        super(parent);
    }

    /**
     * 通过指定配置文件位置创建ApplicationContext。
     * @param configLocation 配置文件的位置。
     * @throws BeansException 如果初始化过程中出现错误。
     */
    public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
        this(new String[]{configLocation}, true, (ApplicationContext)null);
    }

    /**
     * 通过指定多个配置文件位置创建ApplicationContext。
     * @param configLocations 配置文件的位置数组。
     * @throws BeansException 如果初始化过程中出现错误。
     */
    public ClassPathXmlApplicationContext(String... configLocations) throws BeansException {
        this(configLocations, true, (ApplicationContext)null);
    }

    /**
     * 通过指定配置文件位置和父上下文创建ApplicationContext。
     * @param configLocations 配置文件的位置数组。
     * @param parent 父ApplicationContext。
     * @throws BeansException 如果初始化过程中出现错误。
     */
    public ClassPathXmlApplicationContext(String[] configLocations, @Nullable ApplicationContext parent) throws BeansException {
        this(configLocations, true, parent);
    }

    /**
     * 通过指定配置文件位置数组、刷新标志创建ApplicationContext。
     * @param configLocations 配置文件位置数组。
     * @param refresh 是否立即刷新上下文。
     * @throws BeansException 如果初始化过程中出现错误。
     */
    public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh) throws BeansException {
        this(configLocations, refresh, (ApplicationContext)null);
    }

    /**
     * 通过指定配置文件位置数组、刷新标志和父上下文创建ApplicationContext。
     * @param configLocations 配置文件位置数组。
     * @param refresh 是否立即刷新上下文。
     * @param parent 父ApplicationContext。
     * @throws BeansException 如果初始化过程中出现错误。
     */
    public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException {
        super(parent);
        this.setConfigLocations(configLocations);
        if (refresh) {
            this.refresh();
        }
    }

    /**
     * 通过指定路径和类加载配置文件创建ApplicationContext。
     * @param path 配置文件路径。
     * @param clazz 使用该类来定位资源。
     * @throws BeansException 如果初始化过程中出现错误。
     */
    public ClassPathXmlApplicationContext(String path, Class<?> clazz) throws BeansException {
        this(new String[]{path}, clazz);
    }

    /**
     * 通过指定路径数组和类加载配置文件创建ApplicationContext。
     * @param paths 配置文件路径数组。
     * @param clazz 使用该类来定位资源。
     * @param parent 父ApplicationContext。
     * @throws BeansException 如果初始化过程中出现错误。
     */
    public ClassPathXmlApplicationContext(String[] paths, Class<?> clazz, @Nullable ApplicationContext parent) throws BeansException {
        super(parent);
        Assert.notNull(paths, "Path array must not be null");
        Assert.notNull(clazz, "Class argument must not be null");
        this.configResources = new Resource[paths.length];

        for(int i = 0; i < paths.length; ++i) {
            this.configResources[i] = new ClassPathResource(paths[i], clazz);
        }

        this.refresh();
    }

    /**
     * 获取配置资源的数组。
     * @return 配置资源的Resource数组,如果未设置则返回null。
     */
    @Nullable
    protected Resource[] getConfigResources() {
        return this.configResources;
    }
}

ClassPathXmlApplicationContext通过配置文件spring-cfg.xml初始化Spring IoC容器,SpringIoC容器就会根据配置创建Bean了

Spring IoC容器初始化

Spring IoC容器的初始化,可分为两个大步骤,首先是Bean的定义和载入,然后是Spring IoC的初始化,其中Bean定义和载入分为3步,其内容如下:

  • Resource定位,在Spring的开发中,通过XML或注解描述资源定位,SpringIoC容器根据开发者的配置,进行资源定位
  • BeanDefinition载入,Spring根据开发者提供的配置获取对应的POJO的定义和相关配置
  • BeanDefinition注册,将载入的BeanDefinition向SpringIoC容器中注册,然后SpringIoC根据BeanDefinition的内容创建Bean

完成这3步之后,就将BeanDefinition注册到了Spring IoC容器中,但并没有创建和初始化Bean的实例,默认情况下,SpringIoC容器会自动通过BeanDefinition的内容创建Bean实例并完成依赖注入,当然也可以通过一个配置选项lazy-init去改变这步,其含义就是是否延迟初始化Spring Bean,但这个配置选项只有在Bean是单例(singleton)时才有作用,在没有任何配置的情况下为false,也就是Spring IoC容器默认会自动根据BeanDefinition的内容初始化Bean,如果将其设置为true,那么只有当使用Spinrg IoC容器的getBean方法获取它时,才会进行初始化完成依赖注入

Spring Bean生命周期

Spring IoC容器初始化后,Bean被初始化在Spring IoC容器中,所以也有个生命周期,存在一个初始化、存活和销毁的过程;对于一些需要自定义生命周期的Bean,可以插入代码去改变它们的行为,以满足特定的需求,主要是掌握如何在初始化和销毁时加入自定义的方法
在这里插入图片描述
如图所示,SpringIoC容器在执行了初始化和依赖注入后,会执行一定的步骤完成初始化,通过这些步骤就能自定义初始化,而在Spring IoC容器正常关闭的时候,也会执行一定的步骤来关闭容器,释放资源

定义一个BeanPostProcessor接口的实现类,如下代码所示:

package com.ssm.bean;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

/**
 * 自定义BeanPostProcessor实现类,用于在Spring实例化Bean前后进行额外的操作。
 */
public class BeanPostProcessorImpl implements BeanPostProcessor {

    /**
     * 在Bean初始化之前调用的方法。
     *
     * @param bean 将要被初始化的Bean实例。
     * @param beanName Bean的名称。
     * @return 返回经过可能修改后的Bean实例。
     * @throws BeansException 如果处理中发生错误。
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("【" + bean.getClass().getSimpleName() + "】对象" + beanName + "开始实例化");
        return bean;
    }

    /**
     * 在Bean初始化之后调用的方法。
     *
     * @param bean 已经被初始化的Bean实例。
     * @param beanName Bean的名称。
     * @return 返回经过可能修改后的Bean实例。
     * @throws BeansException 如果处理中发生错误。
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("【" + bean.getClass().getSimpleName() + "】对象" + beanName + "实例化完成");
        return bean;
    }
}

再定义一个DisposableBean接口的实现类

package com.ssm.bean;

import org.springframework.beans.factory.DisposableBean;

/**
 * DisposableBeanImpl类实现了DisposableBean接口,
 * 用于在Spring容器关闭时执行一些必要的清理工作。
 */
public class DisposableBeanImpl implements DisposableBean {

    /**
     * destroy方法是DisposableBean接口规定的方法,
     * 用于在bean销毁时执行必要的释放资源操作。
     *
     * @throws Exception 如果在销毁过程中发生异常,则抛出。
     */
    @Override
    public void destroy() throws Exception {
        System.out.println("调用接口DisposableBean的destroy方法");
        // 在这里可以添加具体的资源释放代码
    }

}

spring-cfg.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">


    <!-- 定义一个BeanPostProcessor的实现类,用于bean创建后的处理 -->
    <bean id="beanPostProcessor" class="com.ssm.bean.BeanPostProcessorImpl" />

    <!-- 定义一个DisposableBean的实现类,用于bean销毁前的处理 -->
    <bean id="disposableBean" class="com.ssm.bean.DisposableBeanImpl" />

    <!-- 配置一个具有属性的bean,表示豆浆的原材料配置 -->
    <bean id="source" class="com.ssm.pojo.Source">
        <property name="bean" value="黑豆" />
        <property name="sugar" value="少糖" />
        <property name="size" value="大杯" />
    </bean>

    <!-- 配置一个延迟初始化的bean,表示豆浆机II,使用了初始化和销毁方法 -->
    <bean id="soybeanMilkMakerII" class="com.ssm.pojo.SoybeanMilkMakerII" lazy-init="true" init-method="init" destroy-method="destroy">
        <property name="beverageShop" value="贡茶" />
        <property name="source" ref="source" />
    </bean>
</beans>

在执行这个代码

    /**
     * 通过Spring IoC容器初始化豆奶制作器II,然后使用该对象制作豆奶。
     * 这个方法展示了依赖注入的方式,通过外部配置文件管理对象的创建。
     */
    public static void testSoybeanMilkMakerII() {
        // 初始化Spring的IoC容器
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring-cfg.xml");
        // 从容器中获取SoybeanMilkMakerII类型的Bean实例
        SoybeanMilkMakerII soybeanMilkMakerII = (SoybeanMilkMakerII) ctx.getBean("soybeanMilkMakerII");
        System.out.println(soybeanMilkMakerII.makeSoybeanMilk());
        // 关闭Spring的IoC容器
        ctx.close();
    }

执行结果如下:

"C:\Program Files\Java\jdk-17.0.1\bin\java.exe" "-javaagent:C:\Users\Administrator\AppData\Local\Programs\IntelliJ IDEA Ultimate\lib\idea_rt.jar=51931:C:\Users\Administrator\AppData\Local\Programs\IntelliJ IDEA Ultimate\bin" -Dfile.encoding=UTF-8 -classpath D:\Programs\Java\ssm-src\ssm\target\classes;C:\Users\Administrator\.m2\repository\org\mybatis\mybatis\3.5.16\mybatis-3.5.16.jar;C:\Users\Administrator\.m2\repository\org\javassist\javassist\3.30.2-GA\javassist-3.30.2-GA.jar;C:\Users\Administrator\.m2\repository\cglib\cglib\3.3.0\cglib-3.3.0.jar;C:\Users\Administrator\.m2\repository\org\ow2\asm\asm\9.7\asm-9.7.jar;C:\Users\Administrator\.m2\repository\org\slf4j\slf4j-api\2.0.13\slf4j-api-2.0.13.jar;C:\Users\Administrator\.m2\repository\org\slf4j\slf4j-reload4j\2.0.13\slf4j-reload4j-2.0.13.jar;C:\Users\Administrator\.m2\repository\ch\qos\reload4j\reload4j\1.2.22\reload4j-1.2.22.jar;C:\Users\Administrator\.m2\repository\org\apache\logging\log4j\log4j-core\2.23.1\log4j-core-2.23.1.jar;C:\Users\Administrator\.m2\repository\org\apache\logging\log4j\log4j-api\2.23.1\log4j-api-2.23.1.jar;C:\Users\Administrator\.m2\repository\com\mysql\mysql-connector-j\8.3.0\mysql-connector-j-8.3.0.jar;C:\Users\Administrator\.m2\repository\com\google\protobuf\protobuf-java\3.25.1\protobuf-java-3.25.1.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-core\6.1.6\spring-core-6.1.6.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-jcl\6.1.6\spring-jcl-6.1.6.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-beans\6.1.6\spring-beans-6.1.6.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-context\6.1.6\spring-context-6.1.6.jar;C:\Users\Administrator\.m2\repository\io\micrometer\micrometer-observation\1.12.5\micrometer-observation-1.12.5.jar;C:\Users\Administrator\.m2\repository\io\micrometer\micrometer-commons\1.12.5\micrometer-commons-1.12.5.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-context-support\6.1.6\spring-context-support-6.1.6.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-expression\6.1.6\spring-expression-6.1.6.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-aop\6.1.6\spring-aop-6.1.6.jar com.ssm.main.BeanMain
【DisposableBeanImpl】对象disposableBean开始实例化
【DisposableBeanImpl】对象disposableBean实例化完成
构造方法.....
【Source】对象source开始实例化
【Source】对象source实例化完成
SoybeanMilkMakerII的构造方法
【SoybeanMilkMakerII】调用BeanNameAware接口的setBeanName方法
【SoybeanMilkMakerII】调用BeanFactoryAware接口的setBeanFactory方法
【SoybeanMilkMakerII】调用ApplicationContextAware接口的setApplicationContext方法
【SoybeanMilkMakerII】对象soybeanMilkMakerII开始实例化
【SoybeanMilkMakerII】调用InitializingBean接口的afterPropertiesSet方法
【SoybeanMilkMakerII】执行自定义初始化方法
【SoybeanMilkMakerII】对象soybeanMilkMakerII实例化完成
这是一杯由贡茶饮品店,提供的大杯少糖黑豆
【SoybeanMilkMakerII】执行自定义销毁方法
调用接口DisposableBean的destroy方法

Process finished with exit code 0

从打印日志中可以看到,BeanPostProcessor针对所有Bean,而DisposableBean针对Spring IoC容器销毁过程,其他的接口只针对单一Bean

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

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

相关文章

idea使用前的全局配置,一次配置,多次使用

前提&#xff1a;每次导入一个新的项目&#xff0c;就需要重新设置编码、maven、jdk、git版本等信息。实际每个项目所用到的配置信息是一致的&#xff0c;除非换一家公司&#xff0c;不然不会改动到这些内容。 idea版本&#xff1a;2024.1.1 1.1、全局Maven配置 IDEA启动页面…

ARTS Week 26

Algorithm 本周的算法题为 35. 搜索插入位置 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 示例 1:输入: nums [1,…

自然资源-地质勘查工作的流程梳理

自然资源-地质勘查工作的流程梳理 地质勘查从广义上可理解为地质工作&#xff0c;地质队员就好像是国家宝藏的“寻宝人”&#xff0c;通过地质勘查&#xff0c;为国家找矿&#xff0c;以保障国家能源资源安全和服务国计民生&#xff0c;发挥着地质工作在国民经济建设中的基础性…

【Linux】Centos7配置JDK

1.启动虚拟机、Xshell、Xftp 2.在Xshell中新建一个会话&#xff0c;用于连接到虚拟机中 3.因为虚拟机里自带有JDK&#xff0c;所以需要先卸载自带的JDK 3.1.查询已安装的 jdk 列表 rpm -qa | grep jdk3.2.将查询到的全部删除 yum -y remove XXX&#xff08;上面查询到的 j…

25岁软件工程师:19岁创业,25岁创建自己的工作室,谈一下我对创业的一点思考。

文章目录 &#x1f95d;About Me&#x1f3c0;关于工作室✅我对创业思考 大家好哈&#xff0c;欢迎查看工程师令狐本期节目。这篇文章主要是一篇回忆复盘总结文&#xff0c;复盘总结刚上大学到走向工作这段经历&#xff0c;自己的感悟、感想与收获&#xff0c;期望对读者有所帮…

java.lang.NoSuchMethodException: com.ruoyi.web.controller.test.bean.HeadTeacher

软件开发过程中使用Java反射机制时遇到了下面的问题 com.ruoyi.web.controller.test.bean.HeadTeacher4b9af9a9 com.ruoyi.web.controller.test.bean.HeadTeacher4b9af9a9java.lang.NoSuchMethodException: com.ruoyi.web.controller.test.bean.HeadTeacher.<init>(java…

【网络】gateway 可以提供的一些功能之三 “ 支持Eureka服务发现 ”

一、Eureka是干什么的 Eureka就像是一个电话簿&#xff0c;但是用来存储和管理各种微服务的地址信息。它帮助微服务之间相互发现和交流&#xff0c;就像你想找某人电话号码一样&#xff0c;只需查看电话簿就能找到他们的联系方式。Eureka也可以帮助系统在服务出现问题时自动发现…

C++反汇编——多态,面试题01

文章目录 1.C的三大特性1.1封装1.2继承1.3多态1.3.1 虚函数1.3.2 多态代码反汇编分析。反汇编分析1——基类指针指向子类对象&#xff0c;构造过程。反汇编分析2——基类指针指向子类对象&#xff0c;调用虚函数getPrice()过程。反汇编分析3——基类对象&#xff0c;调用虚函数…

数据库入门(sql文档+命令行)

一.基础知识 1.SQL&#xff08;Structured Query Language&#xff09;结构化查询语言分类&#xff1a; DDL数据定义语言用来定义数据库对象&#xff1a;数据库、表、字段DML数据操作语言对数据库进行增删改查DQL数据查询语言查询数据库中表的信息DCL数据控制语言用来创建数据…

C#中字典Dictionary与自定义类型CustomType之间的转换

C#中字典Dictionary与自定义类型CustomType之间的转换 思路&#xff1a; 可以使用反射System.Reflection来获取类的具体属性&#xff0c; 属性名称就映射字典的键Key。 新建控制台程序DictionaryCustomClassConversionDemo 第一步、新建关键转换类ConversionUtil。 类Con…

FFmpeg常用API与示例(二)—— 解封装与转封装

封装层 封装格式(container format)可以看作是编码流(音频流、视频流等)数据的一层外壳&#xff0c;将编码后的数据存储于此封装格式的文件之内。 封装又称容器&#xff0c;容器的称法更为形象&#xff0c;所谓容器&#xff0c;就是存放内容的器具&#xff0c;饮料是内容&…

机器人种类分析

2000年前&#xff0c;机器人主要应用于工业生产&#xff0c;俗称工业机器人&#xff0c;由示教器操控&#xff0c;帮助工厂释放劳动力&#xff0c;此时的机器人并没有太多智能而言&#xff0c;完全按照人类的命令执行动作&#xff0c;更加关注电气层面的驱动器、伺服电机、减速…

深度剖析:为何跨境卖家纷纷转向自养号测评?

自养号测评&#xff0c;作为跨境电商卖家的一种关键运营策略&#xff0c;具有举足轻重的地位。通过精心策划的自养号测评&#xff0c;卖家能够有效地推动产品销量的飙升、评论数量的积累&#xff0c;并在平台内实现排名的显著上升。这一系列的正面效果进而有助于提升产品的曝光…

每日一题——力扣27. 移除元素(举一反三)

题目链接&#xff1a;https://leetcode.cn/problems/remove-element/description/ 菜鸡写法&#xff1a; // 函数定义&#xff0c;移除数组nums中所有值为val的元素&#xff0c;并返回新的数组长度 int removeElement(int* nums, int numsSize, int val) {// 如果数组长度为…

kernel32.dll丢失要如何解决?电脑kernel32.dll文件下载方法

kernel32.dll丢失要怎么解决才好&#xff1f;其实针对这个问题还是有很多种的解决方法的&#xff0c;只要你明白了kernel32.dll的作用&#xff0c;了解kernel32.dll&#xff0c;那么就可以有很多种方法去解决&#xff0c;下面一起来看看吧。 一.了解kernel32.dll文件 kernel32…

更新、简略高效的用git(Gitee篇)

前提&#xff1a;因为很多编译软件虽然可以连接git&#xff0c;但是操作起来还是比较懵&#xff0c;不同软件有不同的上传git的方式&#xff0c;而且有的连着GitHub有的是Gitee&#xff0c;那么使用Git Bash无疑是万无一失的方式 然后这一篇也仅针对上传Gitee&#xff0c;上传G…

数据结构05:树与二叉树 习题01[C++]

考研笔记整理&#xff0c;本篇作为树与二叉树的基本概念习题&#xff0c;供小伙伴们参考~&#x1f95d;&#x1f95d; 之前的博文链接在此&#xff1a;数据结构05&#xff1a;树与二叉树[C]-CSDN博客~&#x1f95d;&#x1f95d; 第1版&#xff1a;王道书的课后习题~&#x1…

YOLO-World环境搭建推理测试

一、引子 CV做了这么多年&#xff0c;大多是在固定的数据集上训练&#xff0c;微调&#xff0c;测试。突然想起来一句话&#xff0c;I have a dream&#xff01;就是能不能不用再固定训练集上捣腾&#xff0c;也就是所谓的开放词汇目标检测&#xff08;OVD&#xff09;。偶尔翻…

【总结】CE认证详解

文章目录 CE认证 CE作用 适用范围 测试项目 一、2014/30/EU指令&#xff0c;电磁兼容&#xff08;EMC&#xff09;测试项目 二、2014/35/EU指令&#xff0c;低电压&#xff08;LVD&#xff09;测试项目 三、2011/65/EU指令&#xff0c;有害物质&#xff08;RoHS&#xff09…

Linux进程间通信方式

每个进程的用户空间都是独立的&#xff0c;不能相互访问。 所有进程的内核空间(32位系统3G-4G)都是共享的 应用场景 作为缓冲区&#xff0c;处理速度不同的进程之间的数据传输资源共享&#xff1a;多个进程之间共享同样的资源&#xff0c;一个进程对共享数据的修改&#xff0c…