Java框架快速入门: Spring Bean核心配置与实例化详解

本文纲要

一、Bean基础配置

  1. 项目结构
  2. Bean的基本定义与别名
  3. Bean的作用范围(Scope

二、Bean实例化方式

  1. 项目结构
  2. 构造方法实例化
  3. 静态工厂实例化
  4. 实例工厂实例化
  5. FactoryBean实例化

三、Bean生命周期

  1. 项目结构
  2. 配置方式控制生命周期
  3. 接口方式控制生命周期
  4. 关闭容器的两种方式

四、总结

一、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配置需要 idclass 属性。

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-beanfactory-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-methoddestroy-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的配置、创建和管理方式。后面将深入探讨依赖注入的多种形式和细节。

相关推荐
爱敲代码的小鱼1 小时前
springboot原理篇:
java·spring boot·spring
aaPIXa6221 小时前
C++ TensorRT Edge-LLM 边缘推理框架:从原理到实战
开发语言·c++·edge
zzzll11111 小时前
如何学习AI Agent?
java·人工智能·学习·程序员·大模型
阿pin2 小时前
Java随笔-GC收集器工作机制
java·开发语言·jvm·gc
2601_962440842 小时前
计算机毕业设计之jsp旅游网站管理系统
java·开发语言·算法·课程设计·旅游·推荐算法
默默敲代码的徐哥儿2 小时前
八股文整理——后端
java·开发语言·spring boot·后端·学习
matlab代码2 小时前
基于matlab人脸门禁识别系统(可增加其它人脸图像)源码40期】
开发语言·matlab·人脸识别·人脸门禁
k↑2 小时前
SpringCloud + React19 集成Scalar的API文档
后端·spring·spring cloud
折哥的程序人生 · 物流技术专研2 小时前
第2篇:策略模式三大角色拆解与UML
java·设计模式·策略模式·uml·编程入门·行为型模式·扩充系列