深入理解 Spring IoC -- 基于 XML 的 Bean 配置详解

一. 基于xml方式Bean的配置

Spring开发中主要是对Bean的配置,Bean的常用配置一览如下:

xml配置方式 功能描述
<bean id="" class=""> Bean的id和全限定名配置
<bean name=""> 通过name设置Bean的别名,通过别名也能直接获取到Bean实例
<bean scope=""> Bean的作用范围,BeanFactory作为容器时取值singleton和prototype
<bean lazy-init=""> Bean的实例化时机,是否延迟加载。BeanFactory作为容器时无效
<bean init-method=""> Bean实例化后自动执行的初始化方法,method指定方法名
<bean destroy-method=""> Bean实例销毁前的方法,method指定方法名
<bean autowire="byType"> 设置自动注入模式,常用的有按照类型byType,按照名字byName
<bean factory-bean="" factory-method=""/> 指定哪个工厂Bean的哪个方法完成Bean的创建

1.1 基础配置

例如:配置UserDaoImpl由Spring容器负责管理

< bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"></ bean>

此时存储到Spring容器(singleObjects单例池)中的Bean的beanName是userDao,值是UserDaoImpl对象,可

以根据beanName获取Bean实例

applicationContext.getBean("userDao");

如果不配置id,则Spring会把当前Bean实例的全限定名作为beanName

applicationContext.getBean("com.itheima.dao.impl.UserDaoImpl");

1.2 别名配置

2)Bean的别名配置

可以为当前Bean指定多个别名,根据别名也可以获得Bean对象

< bean id="userDao" name="666,aaa"

class="com.itheima.dao.impl.UserDaoImpl"/></ bean>

此时多个名称都可以获得UserDaoImpl实例对象

applicationContext.getBean("userDao");

applicationContext.getBean("aaa");

applicationContext.getBean("666");

1.3 Bean的作用范围

默认情况下,单纯的Spring环境Bean的作用范围有两个:Singleton和Prototype

  • singleton:单例,默认值,Spring容器创建的时候,就会进行Bean的实例化,并存储到容器内部的单例池中,每次getBean时都是从单例池中获取相同的Bean实例;
  • prototype:原型,Spring容器初始化时不会创建Bean实例,当调用getBean时才会实例化Bean,每次getBean都会创建一个新的Bean实例。

< bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl" scope="singleton"/>

当scope设置为singleton时,获得两次对象打印结果是一样的:

1.4 Bean的延迟加载

当lazy-init设置为true时为延迟加载,也就是当Spring容器创建的时候,不会立即创建Bean实例,等待用到时在创

建Bean实例并存储到单例池中去,后续在使用该Bean直接从单例池获取即可,本质上该Bean还是单例的。

< bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl" lazy-init="true"/>

1.5 Bean的初始化和销毁方法配置

Bean在被实例化后,可以执行指定的初始化方法完成一些初始化的操作,Bean在销毁之前也可以执行指定的销毁方法完成一些操作,初始化方法名称和销毁方法名称通过。

< bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl" init-method="init" destroy-method="destroy"/>

java 复制代码
public class UserDaoImpl implements UserDao {
	public UserDaoImpl() { System.out.println("UserDaoImpl创建了..."); }
	public void init(){ System.out.println("初始化方法..."); }
	public void destroy(){ System.out.println("销毁方法..."); }
}

扩展:除此之外,我们还可以通过实现 InitializingBean 接口,完成一些Bean的初始化操作,如下:

java 复制代码
public class UserDaoImpl implements UserDao, InitializingBean {
	public UserDaoImpl() {System.out.println("UserDaoImpl创建了...");}
	public void init(){System.out.println("初始化方法...");}
	public void destroy(){System.out.println("销毁方法...");}
	//执行时机早于init-method配置的方法
	public void afterPropertiesSet() throws Exception {
		System.out.println("InitializingBean..."); 
	}
}

1.6 Bean的实例化配置

Spring的实例化方式主要如下两种:

  • 构造方式实例化:底层通过构造方法对Bean进行实例化。
  • 工厂方式实例化:底层通过调用自定义的工厂方法对Bean进行实例化。
1. 构造方法实例化

构造方式实例化Bean又分为无参构造方法实例化和有参构造方法实例化,Spring中配置的bean几乎都是无参构造该方式,此处不在赘述。下面讲解有参构造方法实例化Bean

java 复制代码
> //有参构造方法 
> public UserDaoImpl(String name){ }

有参构造在实例化Bean时,需要参数的注入,通过<constructor-arg>标签,嵌入在< bean>标签内部提供构造参

数,如下:

java 复制代码
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl">
	<constructor-arg name="name" value="haohao"/>
</bean>
2. 工厂方式实例化

又分为如下三种:

  • 2.1 静态工厂方法实例化Bean
    静态工厂方法实例化Bean,其实就是定义一个工厂类,提供一个静态方法用于生产Bean实例,在将该工厂类及其静态方法配置给Spring即可。
java 复制代码
//工厂类
public class UserDaoFactoryBean {
	//静态工厂方法
	public static UserDao getUserDao(String name){
		//可以在此编写一些其他逻辑代码
		return new UserDaoImpl();
	}
}
java 复制代码
<bean id="userDao" class="com.itheima.factory.UserDaoFactoryBean" factory-method="getUserDao">
	<constructor-arg name="name" value="haohao"/>
</bean>
  • 2.2 实例工厂方法实例化Bean
    实例工厂方法,也就是非静态工厂方法产生Bean实例,与静态工厂方式比较,该方式需要先有工厂对象,在用工厂对象去调用非静态方法,所以在进行配置时,要先配置工厂Bean,在配置目标Bean
java 复制代码
//工厂类
public class UserDaoFactoryBean2 {
	//非静态工厂方法
	public UserDao getUserDao(String name){
		//可以在此编写一些其他逻辑代码
		return new UserDaoImpl();
	}
}
java 复制代码
<!-- 配置实例工厂Bean -->
<bean id="userDaoFactoryBean2" class="com.itheima.factory.UserDaoFactoryBean2"/>
<!-- 配置实例工厂Bean的哪个方法作为工厂方法 -->
<bean id="userDao" factory-bean="userDaoFactoryBean2" factory-method="getUserDao">
	<constructor-arg name="name" value="haohao"/>
</bean>
  • 2.3 定义工厂实现FactoryBean
java 复制代码
public class UserDaoFactoryBean3 implements FactoryBean<UserDao> {
	public UserDao getObject() throws Exception {
		return new UserDaoImpl();
	}
	public Class<?> getObjectType() {
		return UserDao.class;
	}
}

配置FactoryBean交由Spring管理即可

java 复制代码
<bean id="userDao" class="com.itheima.factory.UserDaoFactoryBean3"/>

通过断点观察发现Spring容器创建时,FactoryBean被实例化了,并存储到了单例池singletonObjects中,但是getObject()方法尚未被执行,UserDaoImpl也没被实例化,当首次用到UserDaoImpl时,才调用getObject(),此工厂方式产生的Bean实例不会存储到单例池singletonObjects中,会存储到 factoryBeanObjectCache缓存池中,并且后期每次使用到userDao都从该缓存池中返回的是同一个userDao实例。

注意!!!

  • 如果 FactoryBean 是单例的(默认),它通过 getObject() 创建的对象也会被缓存,每次获取都是同一个实例。
  • 如果 FactoryBean 被配置为 scope="prototype",那么每次调用 getObject() 都会创建一个新的对象,不会被缓存。

1.7 Bean的依赖注入配置

1. 构造方法注入

顾名思义,就是通过类的带参构造方法来完成注入。这种方式在Bean实例化时就必须提供依赖,保证了对象的完整性。

使用标签: <constructor-arg>

常用属性:

  • name:指定构造函数的参数名称(最常用,推荐)。
  • index:指定参数在构造函数中的索引位置(从0开始)。
  • type:指定参数的数据类型(用于区分重载的构造函数)。
  • value:注入基本数据类型和String类型的值。
  • ref:注入其他的Bean类型(引用类型)。
java 复制代码
// 目标类 UserServiceImpl
public class UserServiceImpl implements UserService {    
	private String name;    
	private Integer age;    
	private UserDao userDao;    
	// 有参构造    
	public UserServiceImpl(String name, Integer age, UserDao userDao) { 
		this.name = name;        
		this.age = age;        
		this.userDao = userDao;    
	}    
	// ...省略setter/getter}

XML配置:

java 复制代码
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl">    
	<!-- 通过name属性匹配构造参数名称 -->    
	<constructor-arg name="name" value="张三"/>    
	<constructor-arg name="age" value="18"/>    
	<!-- ref引用容器中已存在的Bean -->    
	<constructor-arg name="userDao" ref="userDao"/>
</bean>
2. Setter方法注入

Spring容器通过调用Bean的Setter方法将依赖注入。这种方式更加灵活,是实际开发中使用频率最高的注入方式。

使用标签: <property>

常用属性:

  • name:指定Setter方法对应的属性名称(注意:是setName去掉set后首字母小写,即name)。
  • value:注入基本数据类型和String类型的值。
  • ref:注入其他的Bean类型。
java 复制代码
// 目标类 UserServiceImpl
public class UserServiceImpl implements UserService {    
	private UserDao userDao;    
	// 必须提供Setter方法    
	public void setUserDao(UserDao userDao) {        
		this.userDao = userDao;    
	}
}

XML配置:

java 复制代码
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl">    
	<!-- 通过property标签调用setUserDao方法 -->    
	<property name="userDao" ref="userDao"/>
</bean>
3. 复杂类型/集合类型的注入

除了基本类型和引用类型,Spring还支持对数组、List、Set、Map、Properties等集合类型进行注入。

java 复制代码
<bean id="collectionBean" class="com.itheima.bean.CollectionBean">
    <!-- 数组注入 -->
    <property name="myStrs">
        <array>
            <value>aaa</value>
            <value>bbb</value>
        </array>
    </property>

    <!-- List集合注入 -->
    <property name="myList">
        <list>
            <value>list1</value>
            <value>list2</value>
        </list>
    </property>

    <!-- Set集合注入 -->
    <property name="mySet">
        <set>
            <value>set1</value>
            <value>set2</value>
        </set>
    </property>

    <!-- Map集合注入 -->
    <property name="myMap">
        <map>
            <entry key="k1" value="v1"/>
            <entry key="k2" value="v2"/>
        </map>
    </property>

    <!-- Properties注入 -->
    <property name="myProps">
        <props>
            <prop key="p1">ppp1</prop>
            <prop key="p2">ppp2</prop>
        </props>
    </property>
</bean>
4. 自动装配(Autowire)

除了手动指定依赖关系,Spring 还提供了自动装配机制。通过 标签的 autowire 属性,Spring 容器可以根据特定的规则,自动为 Bean 注入依赖,从而减少大量的 配置。

常用取值:

  • byName:根据属性名称(Setter方法名去掉set后首字母小写)自动匹配容器中 id/name 相同的 Bean 进行注入。
  • byType:根据属性的数据类型自动匹配容器中类型唯一的 Bean 进行注入。(注意:如果容器中存在多个相同类型的 Bean,会报错)。
  • constructor:与 byType 类似,但作用于构造方法的参数。Spring 会根据构造参数的类型去容器中匹配 Bean。
  • no:默认值,不使用自动装配,必须手动配置。
java 复制代码
<!-- 配置 UserDao -->
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>

<!-- 
    autowire="byName": 
    Spring 会检查 UserServiceImpl 中的属性,发现 userDao 属性,
    然后去容器中寻找 id="userDao" 的 Bean 自动注入。
-->
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl" autowire="byName"/>

区别

1.8 Spring的其他配置标签

Spring 的 xml 标签大体上分为两类,一种是默认标签,一种是自定义标签

  • 默认标签:就是不用额外导入其他命名空间约束的标签,例如 < bean> 标签。
标签 作用
<beans> 一般作为 XML 配置根标签,其他标签都是该标签的子标签
<bean> Bean 的配置标签,上面已经详解了,此处不再阐述
<import> 外部资源导入标签
<alias> 指定 Bean 的别名标签,使用较少
  • 自定义标签:就是需要额外引入其他命名空间约束,并通过前缀引用的标签,例如 < context:property-placeholder/> 标签。
相关推荐
艾莉丝努力练剑1 小时前
【Qt:问题解决】Qt程序入口点错误修复:MinGW ABI兼容问题
java·服务器·开发语言·qt·文件系统·动静态库
程序员黑豆8 小时前
Java 注释详解:单行、多行与文档注释的完整指南
java·前端·ai编程
hey_sml10 小时前
JAVA每日一学---CompletableFuture中thenApply与thenCompose区别详解
java·开发语言
Doraemomo10 小时前
数据结构-环形链表
java·数据结构·链表
萧瑟余晖11 小时前
Java深入解析篇十七之SpringCloud
java·开发语言
是未才11 小时前
从输入 URL 到页面返回:DNS、路由、TLS 与 HTTP 完整链路
java·后端·计算机网络
ttod_qzstudio12 小时前
Java 常用语法极简通关(五):类与对象——字段、方法、构造器、this 与 static
java·开发语言·python
萧瑟余晖13 小时前
Java深入解析篇十七之Spring Security
java·开发语言·spring
不老刘14 小时前
【Java入门】Java服务部署方式全景对比:从java -jar到K8s
java·kubernetes·jar