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的创建 |
Bean取别名
通过name取别名,同样可以获取实例
Bean的范围配置
默认情况下,单纯的Spring环境Bean的作用范围有两个(scope配置):Singleton和Prototype
- singleton:单例,默认值,Spring容器创建的时候,就会进行Bean的实例化,并存储到容器内部的单例池中,每次getBean时都是从单例池中获取相同的Bean实例;
- prototype(默认):原型,Spring容器初始化时不会创建Bean实例,当调用getBean时才会实例化Bean,每次getBean都会创建一个新的Bean实例。
Bean的延迟加载
当lazy-init设置为true时为延迟加载,也就是当Spring容器创建的时候,不会立即创建Bean实例,等待用到时在创建Bean实例并存储到单例池中去,后续在使用该Bean直接从单例池获取即可,本质上该Bean还是单例的
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl" lazy-init="true"/>
ApplicationContext applicationContext = new
ClassPathXmlApplicationContext("applicationContext.xml");
// lazy-init="true"开启,那么只有调用getBean时才会创建Bean实例,而不是之前创建容器时
UserService userService = (UserService) applicationContext.getBean("aaa");
System.out.println(userService);
Bean的初始化和销毁方法配置
Bean在被实例化后,可以通过指定的初始化方法完成一些初始化的操作,Bean在销毁之前也可以执行指定的销毁方法完成一些操作。
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"
init-method="init" destroy-method="destroy"/>
定义init方法和destory方法
public class UserDaoImpl implements UserDao {
public UserDaoImpl() { System.out.println("UserDaoImpl创建了..."); }
public void init() { System.out.println("初始化方法..."); }
public void destroy() { System.out.println("销毁方法..."); }
}
由上图可见,并没有显示销毁方法
关闭容器Bean就销毁,否则bean销毁不了,因为关闭容器是在虚拟机结束之前执行,如果没有close,那么虚拟机关闭,什么都没有了。
扩展:除此之外,我们还可以通过实现 InitializingBean 接口,完成一些Bean的初始化操作,如下:
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配置的方法
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("InitializingBean...");
}
}
使用InitializingBean , DisposableBean接口(推荐)
这样就不需要再Bean标签里写init和destory了
Bean的生命周期
下面是Spring Bean生命周期示例,包括创建、初始化和销毁的代码。
Bean类
创建一个简单的Bean类,并定义初始化和销毁方法:
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.DisposableBean;
public class MyBean implements InitializingBean, DisposableBean {
private String name;
private String version;
private String author;
public MyBean() {
System.out.println("1. 创建对象 - 构造器");
}
public void setName(String name) {
this.name = name;
System.out.println("设置属性 - name: " + name);
}
public void setVersion(String version) {
this.version = version;
System.out.println("设置属性 - version: " + version);
}
public void setAuthor(String author) {
this.author = author;
System.out.println("设置属性 - author: " + author);
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("4. 执行Bean初始化方法 (afterPropertiesSet)");
System.out.println("Bean 信息 - Name: " + name + ", Version: " + version + ", Author: " + author);
}
@Override
public void destroy() throws Exception {
System.out.println("关停 Bean - 销毁方法 (destroy)");
}
public void performTask() {
System.out.println("执行业务操作 - " + name);
}
}
Spring配置
使用XML配置来定义Bean,指定其由接口管理的初始化和销毁方法:
<bean id="myBean" class="com.example.MyBean">
<property name="name" value="Spring Bean"/>
<property name="version" value="1.0"/>
<property name="author" value="John Doe"/>
</bean>
主程序
创建Application
类,加载Spring上下文,使用Bean并关闭上下文以触发销毁阶段:
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Application {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
MyBean myBean = (MyBean) context.getBean("myBean");
// 执行myBean的一些业务
// 关闭上下文,触发销毁方法
((ClassPathXmlApplicationContext) context).close();
}
}
Bean的3种实例化
构造方式实例化:底层通过构造方法对Bean进行实例化
工厂方式实例化:底层通过调用自定义的工厂方法对Bean进行实例化
Bean的实例化配置
构造方式实例化Bean又分为无参构造方法实例化和有参构造方法实例化,Spring中配置的<bean>
几乎都是无参构造方式,但处不在赘述。下面讲解有参构造方法实例化Bean
// 有参构造方法
public UserDaoImpl(String name){
}
在实现类文件里默认有一个无参构造器,如果写一个有参构造器,那么jvm识别不到无参构造器会报错 。
在实现类文件中写入有参构造方法后,有参构造在实例化Bean时,需要参数的注入,通过<constructor-arg>
标签(name表示传入的变量名,value表示变量名对应的传入参数,在bean标签里传入即可,调用getBean会自动添加进去,如果有多个参数,就多个<constructor-arg>标签
),嵌入在<bean>
标签内部提供构造参数,如下:
<!--上面的有参构造器对应的xml配置-->
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl">
<constructor-arg name="name" value="haohao"/>
</bean>
静态工厂创建对象
factory-method 创建对象:即在xml的bean标签里配置factory-method = "userDao",那么创建getBean对象返回的不是class对应的类的实例,而是class类里面userDao静态方法返回的实例
<bean id="userDao1" class="com.itheima.factory.MyBeanFactory1"
factory-method="userDao"></bean>
public class MyBeanFactory1 {
public static UserDao userDao(){
// bean之前可以进行其他的业务逻辑操作
return new UserDaoImpl();
}
}
实例工厂创建对象
<--实例化一个工厂-->
<bean id="myBeanFactory2" class="com.itheima.factory.MyBeanFactory2"
lazy-init="true"></bean>
<--factory-bean是代指工厂-->
<bean id="userDao2" factory-bean="myBeanFactory2"
factory-method="userDao" lazy-init="true"></bean>
public class MyBeanFactory2 {
// 实例方法返回对象
public UserDao userDao(){
return new UserDaoImpl();
}
}
FactoryBean创建(实用)
package com.itheima.factory;
import com.itheima.dao.UserDao;
import com.itheima.dao.impl.UserDaoImpl;
import org.springframework.beans.factory.FactoryBean;
public class UserDaoFactoryBean implements FactoryBean {
// 返回bean
@Override
public UserDao getObject() throws Exception {
return new UserDaoImpl();
}
// 返回Bean的类型
@Override
public Class<?> getObjectType() {
return UserDao.class;
}
// 是否为单例
@Override
public boolean isSingleton() {
return true;
}
}
<bean id="UserDao" class="com.itheima.factory.UserDaoFactoryBean"></bean>
依赖注入的方式
一、setter方法注入引用类型/简单类型(下面以同时注入这两种类型为例)
假设我们有一个Product
类,它包含一个简单类型属性(如productId
)和一个引用类型属性(如Category
)。
1. 定义Category
类
首先,定义Category
类,表示产品类别:
public class Category {
private String categoryName;
// Setter方法
public void setCategoryName(String categoryName) {
this.categoryName = categoryName;
}
// Getter方法
public String getCategoryName() {
return categoryName;
}
public void displayInfo() {
System.out.println("Category: " + categoryName);
}
}
2. 定义Product
类
接下来,定义一个Product
类,它具有一个简单类型的productId
和一个引用类型的Category
:
public class Product {
private String productId; // 简单类型
private Category category; // 引用类型
// Setter方法注入
public void setProductId(String productId) {
this.productId = productId;
}
public void setCategory(Category category) {
this.category = category;
}
public void displayProductInfo() {
System.out.println("Product ID: " + productId);
if (category != null) {
category.displayInfo();
} else {
System.out.println("No category information available.");
}
}
}
3. Spring配置文件
在Spring的XML配置文件中,定义Category
和Product
的Bean,并同时注入简单类型和引用类型:
<!-- applicationContext.xml -->
<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="category" class="com.example.Category">
<property name="categoryName" value="Electronics"/> <!-- 注入简单类型 -->
</bean>
<bean id="product" class="com.example.Product">
<property name="productId" value="P12345"/> <!-- 注入简单类型name是形参 -->
<property name="category" ref="category"/> <!-- 注入引用类型 -->
</bean>
</beans>
4. 主程序
最后,创建一个主程序,通过Spring上下文来获取Product
对象并执行相应操作:
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Application {
public static void main(String[] args) {
// 加载Spring上下文
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// 获取product Bean
Product product = (Product) context.getBean("product");
product.displayProductInfo(); // 显示产品信息
// 关闭上下文
((ClassPathXmlApplicationContext) context).close();
}
}
运行结果
运行程序将输出以下内容:
Product ID: P12345
Category: Electronics
二、构造器注入引用类型/简单类型(下面以同时注入这两种类型为例)
1. 定义Category
类
Category
类保持不变,依然表示产品类别
2. 定义Product
类
将Product
类的构造函数修改为接收两个参数:一个简单类型productId
和一个引用类型Category
。
public class Product {
private String productId; // 简单类型
private Category category; // 引用类型
// 构造函数注入
public Product(String productId, Category category) {
this.productId = productId;
this.category = category;
}
public void displayProductInfo() {
System.out.println("Product ID: " + productId);
if (category != null) {
category.displayInfo();
} else {
System.out.println("No category information available.");
}
}
}
3. Spring配置文件
在Spring的XML配置文件中,使用构造器来注入简单类型和引用类型。我们可以使用constructor-arg
来指定构造函数参数:
<!-- applicationContext.xml -->
<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="category" class="com.example.Category">
<property name="categoryName" value="Electronics"/> <!-- 注入简单类型 -->
</bean>
<bean id="product" class="com.example.Product">
<constructor-arg value="P12345"/> <!-- 注入简单类型 -->
<constructor-arg ref="category"/> <!-- 注入引用类型 -->
</bean>
</beans>
4. 主程序
主程序实例保持不变,只是调用已经不同的注入手段(构造器)来获取Product
对象并执行操作
- 强制依赖使用构造器进行,使用setter注入有概率不进行时导致null对象出现
- 可选依赖使用setter注入进行,灵活性强
- Spring框架倡导使用构造器,第三方框架内部多数采用构造器注入的形式进行数据初始化,相对严谨
- 如果有必要可以同时使用,使用构造器注入完全依赖的注入,使用setter注入完成可选依赖的注入
- 实际开发过程中需要根据实际情况分析,如果受控对象没有提供setter方法就必须使用构造器注入
- 自己开发的模块推荐使用setter注入
依赖自动装配
- 自动装配用于引用类型依赖注入,不能对简单类型进行操作
- 使用按类型装配时(byType)必须保证容器中相同类型的bean唯一,推荐使用
- 使用按名称装配时(byName)必须保证容器中具有指定名称的bean,因为变量名与配置混合,不推荐使用
- 自动装配优先级低于setter注入与构造器注入,同时出现时自动装配配置失效
集合的注入
<!-- 定义一个bean,id为bookDao,对应的实现类为com.itheima.dao.impl.BookDaoImpl -->
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<!-- 配置一个名为array的属性,类型为数组 -->
<property name="array">
<!-- 数组元素 -->
<array>
<!-- 数组中的值 -->
<value>100</value>
<value>200</value>
<value>300</value>
</array>
</property>
<!-- 配置一个名为list的属性,类型为列表 -->
<property name="list">
<!-- 列表元素 -->
<list>
<!-- 列表中的值 -->
<value>itcast</value>
<value>itheima</value>
<value>boxuegu</value>
<value>chuanzhihui</value>
</list>
</property>
<!-- 配置一个名为set的属性,类型为集合 -->
<property name="set">
<!-- 集合元素 -->
<set>
<!-- 集合中的值,注意集合不允许重复值 -->
<value>itcast</value>
<value>itheima</value>
<value>boxuegu</value>
<!-- 重复的值boxuegu将被忽略 -->
<value>boxuegu</value>
</set>
</property>
<!-- 配置一个名为map的属性,类型为映射 -->
<property name="map">
<!-- 映射元素 -->
<map>
<!-- 映射中的键值对 -->
<entry key="country" value="china"/>
<!-- 可以继续添加更多的键值对 -->
</map>
</property>
</bean>