03. Bean的定义与生命周期管理
内容目录

Bean的定义与生命周期管理

学习目标:

  • 学习如何使用 XML 配置、注解(如@Component, @Autowired, @Value等)以及Java配置类来定义Bean和管理依赖关系。
  • 理解Spring容器的工作原理,包括Bean的生命周期管理,如Bean的初始化、销毁回调方法等。

一、Bean的定义与管理

1.1 什么是Bean?

Bean是Spring管理的Java对象。

比如 服务“类”、数据访问对象(DAO)、控制器等,它们由Spring框架自动实例化、装配(即注入依赖)和管理其生命周期。

通过依赖注入(DI)和面向切面编程(AOP)等机制,Spring让这些Bean能够轻松地协同工作,降低了组件之间的耦合度,提升了代码的可测试性和可维护性。

在Spring框架中,定义和管理Bean及其依赖关系可以通过三种主要方式实现:

  • XML配置
  • 注解配置
  • Java配置类

1.2 XML 配置

1.2.1 专业讲解

在Spring框架中,XML配置是一种经典的配置方式,用于定义应用程序组件(Bean)及其相互依赖。

它通过.xml文件来描述Bean的实例化细节,包括Bean的类名、属性设置、构造函数参数、依赖的其他Bean等。

开发者通过 <bean> 标签定义每个Bean,并使用属性和子元素来配置Bean的属性和依赖关系。

Spring容器负责解析这些配置,在运行时管理Bean的生命周期,包括创建、初始化、装配和销毁。

1.2.2 通俗讲解

想象你正在为一个大型晚宴做准备,XML配置就像是晚宴的详细菜单和采购计划。每一道菜(Bean)都被清楚地列出,包括需要的食材(属性)和其他菜品的搭配(依赖)。你把这张菜单交给厨房经理(Spring容器),它负责按照菜单上的说明准备食材、组合菜品,确保每道菜在上桌前都准备妥当,而且宴会结束后还会清理厨房。

1.2.3 开发实际场景举例

假设我们要构建一个简单的电子商务应用,其中有一个 ProductService 需要与 ProductDAO 交互来处理商品信息。

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">

    <!-- 定义ProductDAO Bean -->
    <bean id="productDAO" class="com.example.ProductDAO"/>

    <!-- 定义ProductService Bean,并注入ProductDAO的依赖 -->
    <bean id="productService" class="com.example.ProductService">
        <property name="productDAO" ref="productDAO"/>
    </bean>
</beans>

在这个场景中,ProductServiceProductDAO 是我们的两个组件(Bean),通过XML配置,我们告诉Spring容器如何创建它们以及它们之间的依赖关系。

  • ProductDAO.java 可能是一个接口或抽象类,定义了与数据库交互的方法。
  • ProductService.java 实现业务逻辑,需要依赖ProductDAO来执行数据库操作。

通过上述XML配置,Spring容器会在应用启动时自动创建 ProductDAOProductService 的实例,并将 ProductDAO 的实例注入到 ProductService 中,实现了依赖的自动管理,简化了代码的编写和维护。

1.2.4 简易代码演示

创建一个使用Spring框架通过XML配置Bean的最小项目,我们将遵循以下基本步骤:

项目结构图

(项目结构图)

步骤 1: 创建Maven项目

首先,确保你有Maven环境安装,并使用IDE(如IntelliJ IDEA或Eclipse)创建一个新的Maven项目。

步骤 2: 添加Spring依赖

编辑pom.xml文件,添加Spring框架的核心依赖:

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.23</version>
    </dependency>
</dependencies>

步骤 3: 创建Java类

接下来,创建两个简单的Java类,一个是服务接口,一个是它的实现类。

GreetingService.java(服务接口):

package com.tqazy.service;

public interface GreetingService {
    void sayHello();
}

SimpleGreetingService.java(服务实现类):

package com.tqazy.service;

public class SimpleGreetingService implements GreetingService {
    @Override
    public void sayHello() {
        System.out.println("Hello, Spring!");
    }
}

步骤 4: 创建Spring配置文件

src/main/resources 目录下创建一个名为 applicationContext.xml 的文件,用来配置Bean:

<?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">

    <bean id="greetingService" class="com.tqazy.service.SimpleGreetingService"/>

</beans>

步骤 5: 主程序启动Spring容器并使用Bean

最后,创建一个主类来启动Spring容器,并从容器中获取并使用Bean。

App.java

package com.tqazy;

import com.tqazy.service.GreetingService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        GreetingService greetingService = context.getBean("greetingService", GreetingService.class);
        greetingService.sayHello();
    }
}

步骤 6: 运行项目

现在,运行 App.javamain 方法。你会看到输出 “Hello, Spring!” ,表明Spring成功创建了Bean并调用了它的方法。

注意:这里的 com.tqazy 应替换为你实际的包名。此外,根据你的IDE和Maven配置,可能需要稍作调整,比如确保资源文件被正确识别和加载。

运行结果

1.3 注解配置

1.3.1 专业讲解

注解配置是Spring框架提供的一种现代、简洁的配置方式,用于声明式地定义Bean和管理它们的依赖关系。

主要涉及的注解包括:

想了解这3个注解的具体信息,可以点击下面注解名称或者在本站搜索,有详细文章介绍

  • @Component:这是一个通用的注解,用于标记一个Java类为Spring中的Bean组件。Spring通过扫描特定包路径下的所有带有此注解的类,自动将它们注册为Bean。

  • @Autowired:此注解实现依赖注入,允许Spring自动将匹配的Bean注入到需要它们的字段或方法中。Spring通过类型匹配来决定哪个Bean应该被注入。

  • @Value:用于注入基本类型和String类型的值,可以从属性文件或其他配置源中读取这些值。

1.3.2 通俗讲解

想象你正在组织一场派对,每个人或物品(Bean)都有特定的角色(服务)。

  • @Component 就像是给每个人或物品发放的标牌,标明他们属于这场派对的工作人员或物品,比如厨师、服务员、DJ、派对指定烤箱等。Spring通过查看谁佩戴了标牌,就知道他们是派对的一部分,应该进入会场。

  • @Autowired 好比是自动匹配任务的能力。一旦确定了某个人(如厨师)的角色,Spring就会自动找到他需要的工具(如烤箱、刀具、服务员,即其他Bean),而不用他自己去寻找。厨师只需要专注于烹饪,无需担心工具从哪里来。

  • @Value 则是给这些工作人员分配具体的任务指令。比如给DJ的指令可能是"播放摇滚音乐",这个指令可以直接从派对规划书(配置文件)中读取。

1.3.3 开发实际场景举例

假设你正在开发一个简单的博客系统,包含文章类Article和文章服务类ArticleService

注解配置示例:

// 使用@Component标记为Spring管理的Bean
@Component
public class Article {
    // 文章内容
    private String content;
    // 省略getter和setter
}

@Service
public class ArticleService {
    // 使用@Autowired自动注入Article的依赖
    @Autowired
    private Article article;

    // 业务逻辑方法
    public void publishArticle() {
        // 省略具体实现
    }

    // 从配置文件中注入值
    @Value("${article.author}")
    private String author;
}

在这个例子中,ArticleArticleService 通过 @Component@Service 注解成为了Spring管理的Bean。ArticleService 类中的 article 字段通过 @Autowired 自动注入了 Article 的实例,而 author 字段则通过 @Value 从外部配置(如 application.properties )中读取了博客作者的名字。

1.3.4 简易代码演示

步骤 1: 创建Maven项目

1、打开IDE(如IntelliJ IDEA或Eclipse),创建一个新的Maven项目。

项目结构图

2、在项目的pom.xml中添加Spring框架的依赖:

<dependencies>
    <!-- Spring Core and Context -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.23</version>
    </dependency>
    <!-- Spring AOP for @Configurable support -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>5.3.23</version>
    </dependency>
</dependencies>

步骤 2: 创建Article和ArticleService类

Article.java

package com.tqazy;

import org.springframework.stereotype.Component;

@Component
public class Article {
    private String content;

    public Article() {
        this.content = "这是文章类的构造器添加的测试内容。";
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }
}

ArticleService.java

package com.tqazy;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Value;

@Service
public class ArticleService {
    private final Article article;
    private String author;

    @Autowired
    public ArticleService(Article article) {
        this.article = article;
    }

    @Value("${article.author}")
    public void setAuthor(String author) {
        this.author = author;
    }

    public void publishArticle() {
        System.out.println("文章作者:" + author + ",文章内容:" + article.getContent());
    }
}

步骤 3: 创建Spring配置类

因为没有使用Spring Boot,我们需要一个配置类来代替自动配置的功能。

创建SpringConfig.java:

package com.tqazy;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

@Configuration
@ComponentScan(basePackages = "com.tqazy")
@PropertySource("classpath:application.properties")
public class SpringConfig {

    // 如果需要,可以在这里通过@Bean手动定义Bean,但因为我们使用@Component等注解,所以这里可以省略
}

步骤 4: 添加属性文件

src/main/resources 目录下创建 application.properties

article.author=tqazy

步骤 5: 主函数及运行

创建主函数以手动启动Spring上下文,并从中获取Bean进行测试。

创建App.java:

package com.tqazy;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class App {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        ArticleService articleService = context.getBean(ArticleService.class);
        articleService.publishArticle();
    }
}

运行结果

这个项目中,不用关注配置类什么的,只需要关注3个注解的使用。

1.4 Java配置类

1.4.1 专业讲解

Java配置类是Spring框架提供的另一种配置方式,它使用Java类而非XML文件来定义和管理Bean及其依赖关系。

这种方式更加灵活,支持复杂的逻辑和条件配置,并且与Java生态系统更加自然地集成。

Java配置类通过使用 @Configuration@Bean 等注解来实现:

  • @Configuration 注解标记一个类作为配置类,该类可以包含一个或多个 @Bean 方法。
  • @Bean 注解在一个方法上,表示该方法返回的对象应该被注册为一个Spring管理的Bean,可以指定Bean的名称、初始化方法、销毁方法等。

1.4.2 通俗讲解

想象你正在规划一个生日派对,而Java配置类就像是派对策划师,它通过一系列指令(方法)来告诉系统(厨房、装饰团队等)需要准备什么(Bean)以及如何准备(依赖关系)。

@Configuration 就像是给派对策划师的认证,表明这个人(类)有权制定派对的所有安排。
@Bean 方法就像是策划师说:“我们需要一个生日蛋糕,做法是这样的…”,每当这样的方法被执行,就相当于制作了一份特别的物品(Bean),并且系统会记住这份物品的制作方法和成品,供后续使用。

1.4.3 开发实际场景举例

假设你正在开发一个用户认证模块,需要配置用户服务层类 UserService 和用户持久层类 UserRepository

// Java配置类示例
import com.example.repository.UserRepository;
import com.example.service.UserService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {

    // 模拟数据库连接配置,实际中可能来自外部配置源
    private final String databaseUrl = "jdbc:mysql://localhost:3306/mydb";

    // 定义UserRepository Bean
    @Bean
    public UserRepository userRepository() {
        // 假设这里是创建并初始化UserRepository实例的逻辑,可能包括数据库连接等
        return new UserRepository(databaseUrl);
    }

    // 定义UserService Bean,并注入UserRepository依赖
    @Bean
    public UserService userService(UserRepository userRepository) {
        // 创建UserService实例时,自动注入UserRepository
        return new UserService(userRepository);
    }
}

在这个例子中,AppConfig 类被 @Configuration 注解标记为配置类。

userRepository()userService() 方法分别定义了两个Bean,其中 userService() 方法的参数 userRepository 由Spring自动注入,体现了依赖关系的管理。

这种方式使得配置更加类型安全,易于理解和维护,尤其是当项目复杂度上升时,Java配置的优势更为明显。

1.4.4 简易代码演示

步骤1:创建Maven项目并配置Spring

1、创建Maven项目:

使用IDE(如IntelliJ IDEA或Eclipse)创建一个新的Maven项目。

项目结构图

2、配置pom.xml:

在pom.xml中添加Spring核心库的依赖:

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.23</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>5.3.23</version>
    </dependency>
</dependencies>

步骤2:创建User模型、持久层(简易实现)和业务服务层及实现

1、User.java(用户实体):

package com.tqazy.model;

public class User {
    private String username;
    private String password;

    public User() {
    }

    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }
    // get、set方法省略
}

2、UserRepository.java(用户仓库接口):

package com.tqazy.repository;

import com.tqazy.model.User;

public interface UserRepository {
    User findByUsername(String username);
}

3、InMemoryUserRepository.java(内存中的用户仓库实现,仅用于演示):

package com.tqazy.repository;

import com.tqazy.model.User;

public class InMemoryUserRepository implements UserRepository {

    public InMemoryUserRepository(String databaseUrl) {
        System.out.println("databaseUrl=" + databaseUrl);
    }

    @Override
    public User findByUsername(String username) {
        // 简化示例,实际应从数据库查询
        if ("testUser".equals(username)) {
            return new User("testUser", "password");
        }
        return null;
    }
}

4、UserService.java(用户服务接口):

package com.tqazy.service;

import com.tqazy.model.User;

public interface UserService {
    User authenticate(String username, String password);
}

5、UserServiceImpl.java(用户服务实现):

package com.tqazy.service;

import com.tqazy.model.User;
import com.tqazy.repository.UserRepository;

public class UserServiceImpl implements UserService {
    private final UserRepository userRepository;

    public UserServiceImpl(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    public User authenticate(String username, String password) {
        User user = userRepository.findByUsername(username);
        if (user != null && user.getPassword().equals(password)) {
            return user;
        }
        return null;
    }
}

步骤3:创建Spring配置类

AppConfig.java

package com.tqazy.config;

import com.tqazy.repository.InMemoryUserRepository;
import com.tqazy.repository.UserRepository;
import com.tqazy.service.UserService;
import com.tqazy.service.UserServiceImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {

    // 模拟从外部配置获取的数据库连接信息,实际项目中使用@Value读取配置文件
    private static final String DATABASE_URL = "jdbc:mysql://localhost:3306/mydb";

    // 示例中使用内存中的用户仓库,但配置方式展示了如何向构造函数传参
    @Bean
    public UserRepository userRepository() {
        // 实际项目中,您可能会创建DataSource并传递给真正的Repository实现
        return new InMemoryUserRepository(DATABASE_URL);
    }

    // 定义UserService Bean,使用构造器注入UserRepository
    @Bean
    public UserService userService(UserRepository userRepository) {
        return new UserServiceImpl(userRepository);
    }
}

步骤4:编写主函数

App.java

package com.tqazy;

import com.tqazy.config.AppConfig;
import com.tqazy.model.User;
import com.tqazy.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class App {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        UserService userService = context.getBean(UserService.class);

        User authenticatedUser = userService.authenticate("testUser", "password");
        if (authenticatedUser != null) {
            System.out.println("用户认证成功: " + authenticatedUser.getUsername());
        } else {
            System.out.println("认证失败。");
        }
    }
}

步骤5:启动并运行App类的main方法

运行结果

二、Spring容器的工作原理

2.1 专业讲解

Spring容器是Spring框架的核心,负责管理和组织Bean的生命周期。

它通过读取配置(XML、注解、Java配置类)来创建Bean实例,管理它们之间的依赖关系,并提供必要的服务,如资源管理、事务管理等。

Spring容器的工作流程大致分为Bean定义的载入、Bean实例化、依赖注入、初始化、使用、销毁几个阶段。

Bean的生命周期管理包括:

  • Bean定义载入: Spring容器从配置源(如XML文件、注解、Java配置类)读取Bean定义信息。
  • Bean实例化: 根据Bean定义,Spring使用反射机制创建Bean实例。
  • 依赖注入: 容器遍历Bean定义中的依赖关系,通过属性填充或构造器注入等方式,将其他Bean实例注入到当前Bean中。
  • 初始化: Bean实例创建后,会调用自定义的初始化方法(通过 @PostConstruct 注解或 InitializingBean 接口指定),进行额外的初始化操作。
  • 使用: Bean已准备好并可被应用程序代码使用。
  • 销毁回调: 当容器关闭或Bean不再需要时,会调用预定义的销毁方法(通过 @PreDestroy 注解或 DisposableBean 接口指定),执行清理操作。

2.2 通俗讲解

想象Spring容器是一个高级管家,负责照顾你家里(应用)的各种物品(Bean)。

  • Bean定义载入: 就像管家先查看清单(配置文件),了解家里需要哪些物品,它们放在哪里,有什么特性。
  • Bean实例化: 接着,管家根据清单购置物品,摆放到对应位置(内存中)。
  • 依赖注入: 如果某个物品需要搭配其他物品使用(依赖关系),管家会自动将它们放在一起。比如将电池放在手电筒中。
  • 初始化: 对于一些需要组装或特殊准备的物品,管家还会进行额外的安装和调试。
  • 使用: 一切就绪后,家庭成员(应用代码)就可以直接使用这些物品了。
  • 销毁回调: 当某物品不再需要或搬家(应用关闭)时,管家会负责妥善处理这些物品,比如拆卸、回收。

2.3 开发实际场景举例

假设有一个简单的Web应用,包含一个 UserService 类,它依赖于 UserRepository 来操作数据库。

@Service
public class UserService {
    // 业务方法:模拟业务
    public void findUserById(Long id) {
        System.out.println("找到根据" + id +"找到用户了");
    }

    // 初始化回调方法
    @PostConstruct
    public void init() {
        System.out.println("UserService 初始化完成");
    }

    // 销毁回调方法
    @PreDestroy
    public void destroy() {
        System.out.println("UserService 正在销毁");
    }
}

在这个例子中,UserService 类通过构造器注入的方式依赖于 UserRepository

同时定义了 @PostConstruct@PreDestroy 注解的方法,分别表示Bean的初始化和销毁回调。

2.4 简易代码演示

基于上面的 UserService.java,创建一个简单的maven+spring程序,调用 UserService.javafindUserById 方法。运行结果如下:

传参id为1:

运行结果

版权声明:本文《03. Bean的定义与生命周期管理》是由陶其原创撰写,首发于陶其的个人博客
转载声明:如需转载本文,请务必在转载处保留原文链接:https://www.tqazy.com/?p=78,并明确注明文章来源。

评论

  1. 李庆林
    Macintosh Chrome
    4 月前
    2024-5-28 19:06:20

    陶总 666👍

    • 博主
      李庆林
      Windows Chrome
      4 月前
      2024-5-28 21:53:13

      客气了,李总,向李总学习٩(ˊᗜˋ*)و

发送评论 编辑评论

|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇