本文纲要
一、Bean基础配置
- 项目结构
Bean的基本定义与别名Bean的作用范围(Scope)
二、Bean实例化方式
- 项目结构
- 构造方法实例化
- 静态工厂实例化
- 实例工厂实例化
FactoryBean实例化
三、Bean生命周期
- 项目结构
- 配置方式控制生命周期
- 接口方式控制生命周期
- 关闭容器的两种方式
四、总结
一、Bean基础配置
1 )项目结构
dir
spring_02_base_config
├── pom.xml
└── src
└── main
├── java
│ └── com
│ └── wb
│ ├── AppForName.java
│ ├── AppForScope.java
│ ├── dao
│ │ ├── BookDao.java
│ │ └── impl
│ │ └── BookDaoImpl.java
│ └── service
│ ├── BookService.java
│ └── impl
│ └── BookServiceImpl.java
└── resources
└── applicationContext.xml
在Spring中,Bean的配置信息主要包含以下三部分:
- id / name:Bean的标识与别名
- class:Bean的类型(全限定类名)
- scope:Bean的作用范围
下面将围绕这三部分展开讲解,并演示相关配置与代码。
2 ) Bean的基本定义与别名
一个最基本的Bean配置需要 id 和 class 属性。
xml
<bean id="bookService" class="com.wb.service.impl.BookServiceImpl">
<property name="bookDao" ref="bookDao"/>
</bean>
id:Bean的唯一标识,在容器中通过该名称获取Bean。class:指定Bean的具体实现类。
别名配置
可以在同一个Bean定义中通过 name 属性为其指定多个名称,多个别名之间可以使用逗号、分号或空格分隔。
xml
<bean id="bookService" name="service service4 bookEbi" class="com.wb.service.impl.BookServiceImpl">
<property name="bookDao" ref="bookDao"/>
</bean>
此时,"bookService"、"service"、"service4"、"bookEbi" 四个名称在容器中均指向同一个Bean对象。
测试代码
java
package com.wb;
import com.wb.dao.BookDao;
import com.wb.service.BookService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AppForName {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
// 直接使用别名 "service4" 获取Bean
BookService bookService = (BookService) ctx.getBean("service4");
bookService.save();
}
}
如果使用了不存在的名称(如 "service4" 写错),Spring会抛出 NoSuchBeanDefinitionException,提示 No bean named 'xxx' available。这时需要检查配置文件中的Bean名称或代码中的 getBean 参数。
3 ) Bean的作用范围(Scope)
通过 scope 属性可以控制Spring容器创建的Bean实例是单例(singleton)还是多例(prototype)。
xml
<!-- 默认scope="singleton",可不写 -->
<bean id="bookDao" name="dao" class="com.wb.dao.impl.BookDaoImpl" scope="prototype"/>
| scope值 | 说明 |
|---|---|
singleton(默认) |
单例,容器中只有一个实例,多次获取返回同一个对象 |
prototype |
多例,每次从容器获取都创建新的实例 |
测试代码
java
package com.wb;
import com.wb.dao.BookDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AppForScope {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
BookDao bookDao1 = (BookDao) ctx.getBean("bookDao");
BookDao bookDao2 = (BookDao) ctx.getBean("bookDao");
System.out.println(bookDao1); // 打印对象地址
System.out.println(bookDao2);
}
}
- 当
scope="singleton"时,两次获取的地址相同。 - 当
scope="prototype"时,两次获取的地址不同。
为什么默认是单例?
Spring容器管理的Bean通常为可复用的组件(如Service、DAO),减少重复创建对象的开销,提升性能。而需要频繁变更状态的对象(如封装数据的Domain对象)一般不交给Spring管理。
二、Bean实例化方式
Spring提供了多种Bean实例化方式,核心都是通过构造方法或工厂方法创建对象。
1 ) 项目结构
dir
spring_03_base_instance
├── pom.xml
└── src
└── main
├── java
│ └── com
│ └── wb
│ ├── AppForInstanceBook.java
│ ├── AppForInstanceOrder.java
│ ├── AppForInstanceUser.java
│ ├── dao
│ │ ├── BookDao.java
│ │ ├── OrderDao.java
│ │ ├── UserDao.java
│ │ └── impl
│ │ ├── BookDaoImpl.java
│ │ ├── OrderDaoImpl.java
│ │ └── UserDaoImpl.java
│ └── factory
│ ├── OrderDaoFactory.java
│ ├── UserDaoFactory.java
│ └── UserDaoFactoryBean.java
└── resources
└── applicationContext.xml
2 ) 构造方法实例化
Spring默认使用无参构造方法创建Bean实例。
实现类必须有可访问的构造方法 (不写或写 public 无参构造均可,即使私有也可以通过反射调用)。若提供了有参构造而未显式提供无参构造,初始化时会抛出异常。
BookDaoImpl 示例
java
package com.wb.dao.impl;
import com.wb.dao.BookDao;
public class BookDaoImpl implements BookDao {
public BookDaoImpl() {
System.out.println("book dao constructor is running ....");
}
public void save() {
System.out.println("book dao save ...");
}
}
配置
xml
<bean id="bookDao" class="com.wb.dao.impl.BookDaoImpl"/>
使用
java
package com.wb;
import com.wb.dao.BookDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AppForInstanceBook {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
bookDao.save();
}
}
如果缺少无参构造方法,会抛出 BeanCreationException,进一步查看最底层异常可发现 NoSuchMethodException,指明缺少无参构造。阅读异常时建议从最底层开始,逐层向上追溯。
3 ) 静态工厂实例化
有些遗留系统可能通过静态工厂方法创建对象,Spring可配置其作为Bean的实例化方式。
静态工厂类
java
package com.wb.factory;
import com.wb.dao.OrderDao;
import com.wb.dao.impl.OrderDaoImpl;
public class OrderDaoFactory {
public static OrderDao getOrderDao(){
System.out.println("factory setup....");
return new OrderDaoImpl();
}
}
配置
xml
<bean id="orderDao" class="com.wb.factory.OrderDaoFactory" factory-method="getOrderDao"/>
class指向工厂类。factory-method指定静态工厂方法名。
对比传统调用
java
// 传统方式:直接调用静态工厂
// OrderDao orderDao = OrderDaoFactory.getOrderDao();
// orderDao.save();
// Spring方式
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
OrderDao orderDao = (OrderDao) ctx.getBean("orderDao");
orderDao.save();
4 ) 实例工厂实例化
实例工厂需要先有工厂对象,再调用其实例方法创建目标对象。
实例工厂类
java
package com.wb.factory;
import com.wb.dao.UserDao;
import com.wb.dao.impl.UserDaoImpl;
public class UserDaoFactory {
public UserDao getUserDao(){
return new UserDaoImpl();
}
}
配置:先定义工厂Bean,再通过 factory-bean 和 factory-method 指定目标Bean。
xml
<!-- 工厂实例 -->
<bean id="userFactory" class="com.wb.factory.UserDaoFactory"/>
<!-- 目标Bean -->
<bean id="userDao" factory-method="getUserDao" factory-bean="userFactory"/>
对比传统调用
java
// 传统方式
// UserDaoFactory userDaoFactory = new UserDaoFactory();
// UserDao userDao = userDaoFactory.getUserDao();
// userDao.save();
// Spring方式
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = (UserDao) ctx.getBean("userDao");
userDao.save();
5 ) FactoryBean实例化
Spring针对实例工厂的缺点(需单独定义工厂Bean、每次指定方法名)提供了 FactoryBean 接口,简化配置。
实现 FactoryBean 接口
java
package com.wb.factory;
import com.wb.dao.UserDao;
import com.wb.dao.impl.UserDaoImpl;
import org.springframework.beans.factory.FactoryBean;
public class UserDaoFactoryBean implements FactoryBean<UserDao> {
// 代替原始实例工厂中创建对象的方法
public UserDao getObject() throws Exception {
return new UserDaoImpl();
}
// 返回创建对象的类型
public Class<?> getObjectType() {
return UserDao.class;
}
// 可覆盖 isSingleton() 控制单例/多例,默认true(单例)
}
配置:与普通Bean完全相同。
xml
<bean id="userDao" class="com.wb.factory.UserDaoFactoryBean"/>
容器识别出该类实现了 FactoryBean,自动调用 getObject() 方法返回的对象作为最终的Bean,而不是 UserDaoFactoryBean 本身。
演示单例/多例切换
若想改为多例,可以覆盖 isSingleton() 返回 false:
java
@Override
public boolean isSingleton() {
return false;
}
测试代码可获取两次实例对比地址,验证原型效果。
三、Bean生命周期
Bean的生命周期指从对象创建到销毁的过程
Spring允许在初始化和销毁阶段插入自定义操作
1 ) 项目结构
dir
spring_04_bean_lifecycle
├── pom.xml
└── src
└── main
├── java
│ └── com
│ └── wb
│ ├── AppForLifeCycle.java
│ ├── dao
│ │ ├── BookDao.java
│ │ └── impl
│ │ └── BookDaoImpl.java
│ └── service
│ ├── BookService.java
│ └── impl
│ └── BookServiceImpl.java
└── resources
└── applicationContext.xml
2 ) 配置方式控制生命周期
在实现类中定义初始化与销毁方法(方法名任意,必须无参),然后在配置文件中通过 init-method 和 destroy-method 指定。
BookDaoImpl 示例
java
package com.wb.dao.impl;
import com.wb.dao.BookDao;
public class BookDaoImpl implements BookDao {
public void save() {
System.out.println("book dao save ...");
}
// 表示bean初始化对应的操作
public void init(){
System.out.println("init...");
}
// 表示bean销毁前对应的操作
public void destroy(){
System.out.println("destroy...");
}
}
配置
xml
<bean id="bookDao" class="com.wb.dao.impl.BookDaoImpl" init-method="init" destroy-method="destroy"/>
测试代码
java
package com.wb;
import com.wb.dao.BookDao;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AppForLifeCycle {
public static void main( String[] args ) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
bookDao.save();
// 关闭容器,触发销毁方法
ctx.close();
}
}
运行结果:
log
init...
book dao save ...
destroy...
注意:销毁方法只有在单例Bean且显式关闭容器时才会执行,prototype 作用域的Bean由客户端负责销毁,Spring不会调用其销毁方法。
3 ) 接口方式控制生命周期
实现Spring提供的生命周期接口,无需额外配置。
InitializingBean→ 实现afterPropertiesSet()DisposableBean→ 实现destroy()
BookServiceImpl 示例
java
package com.wb.service.impl;
import com.wb.dao.BookDao;
import com.wb.service.BookService;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
public class BookServiceImpl implements BookService, InitializingBean, DisposableBean {
private BookDao bookDao;
public void setBookDao(BookDao bookDao) {
System.out.println("set .....");
this.bookDao = bookDao;
}
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
public void destroy() throws Exception {
System.out.println("service destroy");
}
public void afterPropertiesSet() throws Exception {
System.out.println("service init");
}
}
配置(无需 init-method / destroy-method)
xml
<bean id="bookService" class="com.wb.service.impl.BookServiceImpl">
<property name="bookDao" ref="bookDao"/>
</bean>
容器会在属性注入后自动调用 afterPropertiesSet(),在关闭时调用 destroy()。
生命周期执行顺序图示:
Bean Container Bean Container #mermaid-svg-gUPzhBiPBIUrC7oo{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-gUPzhBiPBIUrC7oo .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-gUPzhBiPBIUrC7oo .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-gUPzhBiPBIUrC7oo .error-icon{fill:#552222;}#mermaid-svg-gUPzhBiPBIUrC7oo .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-gUPzhBiPBIUrC7oo .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-gUPzhBiPBIUrC7oo .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-gUPzhBiPBIUrC7oo .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-gUPzhBiPBIUrC7oo .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-gUPzhBiPBIUrC7oo .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-gUPzhBiPBIUrC7oo .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-gUPzhBiPBIUrC7oo .marker{fill:#333333;stroke:#333333;}#mermaid-svg-gUPzhBiPBIUrC7oo .marker.cross{stroke:#333333;}#mermaid-svg-gUPzhBiPBIUrC7oo svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-gUPzhBiPBIUrC7oo p{margin:0;}#mermaid-svg-gUPzhBiPBIUrC7oo .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-gUPzhBiPBIUrC7oo text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-gUPzhBiPBIUrC7oo .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-gUPzhBiPBIUrC7oo .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-gUPzhBiPBIUrC7oo .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-gUPzhBiPBIUrC7oo .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-gUPzhBiPBIUrC7oo #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-gUPzhBiPBIUrC7oo .sequenceNumber{fill:white;}#mermaid-svg-gUPzhBiPBIUrC7oo #sequencenumber{fill:#333;}#mermaid-svg-gUPzhBiPBIUrC7oo #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-gUPzhBiPBIUrC7oo .messageText{fill:#333;stroke:none;}#mermaid-svg-gUPzhBiPBIUrC7oo .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-gUPzhBiPBIUrC7oo .labelText,#mermaid-svg-gUPzhBiPBIUrC7oo .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-gUPzhBiPBIUrC7oo .loopText,#mermaid-svg-gUPzhBiPBIUrC7oo .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-gUPzhBiPBIUrC7oo .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-gUPzhBiPBIUrC7oo .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-gUPzhBiPBIUrC7oo .noteText,#mermaid-svg-gUPzhBiPBIUrC7oo .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-gUPzhBiPBIUrC7oo .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-gUPzhBiPBIUrC7oo .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-gUPzhBiPBIUrC7oo .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-gUPzhBiPBIUrC7oo .actorPopupMenu{position:absolute;}#mermaid-svg-gUPzhBiPBIUrC7oo .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-gUPzhBiPBIUrC7oo .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-gUPzhBiPBIUrC7oo .actor-man circle,#mermaid-svg-gUPzhBiPBIUrC7oo line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-gUPzhBiPBIUrC7oo :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Bean可供使用 调用构造方法实例化注入属性(setter)afterPropertiesSet() / init-method执行业务方法destroy() / destroy-method(关闭容器时)
4 ) 关闭容器的两种方式
为了观察销毁方法的执行,需要主动关闭Spring容器。
方式一:直接调用 close()
java
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
// 使用Bean...
ctx.close(); // 暴力关闭,容器立即销毁,后续不能再使用
方式二:注册关闭钩子(推荐)
java
ctx.registerShutdownHook(); // JVM退出前自动关闭容器
关闭钩子可以在任意位置注册,保证在JVM退出时回调关闭逻辑。
四、总结
| 主题 | 要点 |
|---|---|
| Bean基础配置 | id + class 定义Bean;name 提供多个别名(逗号/分号/空格分隔);scope 控制单例/原型,默认单例。 |
| **Bean实例化 ** | 1. 构造方法(默认无参);2. 静态工厂(factory-method);3. 实例工厂(factory-bean + factory-method);4. FactoryBean 接口(简化实例工厂,推荐)。 |
| Bean生命周期 | 可通过配置 init-method / destroy-method 或实现 InitializingBean / DisposableBean 接口控制;关闭容器调用 close() 或 registerShutdownHook() 触发销毁。 |
通过以上内容,可以全面掌握Spring核心容器中Bean的配置、创建和管理方式。后面将深入探讨依赖注入的多种形式和细节。