Spring Framework IoC Bean生命周期

目录

spring

1.spring概述

1.1 Spring定义

1.2 Spring核心

2 入门案例

2.1 入门案例

User

TestUser

3 相关概念

3.1 IoC

3.2 IoC容器

3.3 依赖注入DI

4 IoC容器实现

AnnotationConfigApplicationContext 常用

ClassPathXmlApplicationContext 比较繁琐

IoC具体实现流程

5 依赖注入DI

6 注解管理Bean

6.1 Bean对象定义及获取

6.1.1 Bean对象定义

6.1.2 Bean对象获取

6.1.3 应用分析

6.2 依赖注入DI

6.2.1 @Value注解

6.2.2 @Autowired注解

6.2.2.1 根据类型注入

6.2.2.2 根据接口类型注入

6.2.3 @Qualifier注解

6.2.4 @Resource注解(了解)

6.3 Bean作用域

6.3.1 说明

6.3.2 单实例与多实例

6.3.3 应用分析

6.4 Bean生命周期

6.4.1 说明

6.4.2 完整生命周期

6.4.3 生命周期验证

6.4.4 生命周期扩展

6.5 引用外部属性文件

6.5.1 说明

6.5.2 使用流程

6.5.3 应用分析

6.6 自动扫描配置

6.6.1 说明

自动扫描配置是 Spring 框架提供的一种基于注解(Annotation)的配置方式,用于自动发现和注册 Spring 容器中的组件。当我们使用自动扫描配置的时候,只需要在需要被 Spring 管理的组件(比如 Service、Controller、Repository 等)上添加对应的注解,Spring 就会自动地将这些组件注册到容器中,从而可以在其它组件中使用它们。

6.6.2 使用示例

6.6.3 应用分析

常见异常

1 NoSuchBeanDefinitionException

2 NoUniqueBeanDefinitionException


spring

1.spring概述

1.1 Spring定义

Spring是一款主流的 Java EE 开源框架,目的是用于简化Java企业级引用的开发难度和开发周期。从 简单性、可测试性的角度而言,任何Java应用都可以从Spring中受益。Spring框架提供自己提供功能 外,还提供整合其他技术和框架的能力。

自2004年4月,Spring1.0 版正式发布以来,Spring已经步入到了第6个大版本,即 Spring6,本课程 采用 Spring5.3.24 正式版本。

1.2 Spring核心

Spring指的是Spring Framework,通常我们称之为Spring框架。

Spring的两个核心模块:

  • IoC控制反转
    Inverse of Control 的简写,为 控制反转,指把创建对象交给 Spring 进行管理。
    即:反转资源获取方向,把自己创建资源、向环境索取资源的方式变为环境自动将资源准备好,我们享受资源注入。
  • AOP面向切面编程
    Aspect Oriented Programming 的简写,为 面向切面编程。AOP 用来封装多个类的公共行为,将那些与业务无关,却为业务模块共同调用的逻辑封装起来,减少系统的重复代码。

2 入门案例

工程环境

  • JDK版本:Java 8
  • Spring版本:5.3.24

2.1 入门案例

  • 第1步:聚合工程下构建子工程 _03spring
  • 第2步:pom.xml中添加依赖并刷新 Maven
<dependencies>
<!-- Maven坐标:
https://mvnrepository.com/artifact/org.springframework/spring-context -->
<!-- 引入spring context依赖-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.24</version>
    </dependency>
</dependencies>
<properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

注意:根据spring框架支持的jdk来,当前版本用jdk8,否则运行项目时候会报错

User
package com.lhd.spring.example;

import org.springframework.stereotype.Component;

/**
 * @author lhd
 * @date 2024/4/16
 * @apiNote
 */
// @Component 注解 将该类交给IoC容器管理
@Component
public class User {

    public void run(){
        System.out.println("开始学习spring啦");
    }
}
TestUser


import com.lhd.spring.example.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * @author lhd
 * @date 2024/4/16
 * @apiNote
 */
public class TestUser {
    public static void main(String[] args) {
     /*
        1. 创建IoC容器 : Spring Framework 会将创建的 Spring Bean 对象放入IoC容器中;
        2. 获取Bean对象:从IoC容器中获取Spring Framework创建好的对象;
            Spring Framework会根据类型自动注入依赖对象
        3.调用方法测试
      */
        ApplicationContext context = new AnnotationConfigApplicationContext("com.lhd.spring.example");
        User user = context.getBean(User.class);
        user.run();

    }
}

运行后结果

3 相关概念

3.1 IoC

IoC(Inversion of Control,控制反转)是一种编程思想;

IoC 是将对象的创建和管理交由框架来完成,而不是由开发人员手动创建和管理。

3.2 IoC容器

IoC容器是用来实现IoC思想的一个工具或者说技术手段

它能够自动扫描应用程序中的对象,将它们实例化,并自动注入它们所需要的依赖对象,使应用程序的开发人员能够更加专注于业务逻辑的实现,而不用关心对象的创建和管理。 Spring通过IoC容器来管理所有的Java对象的实例化和初始化,控制着对象与对象之间的依赖关系。

我们将由IoC容器管理的Java对象成为 Spring Bean,它与使用关键字 new 创建的Java对象没有任何区别。

  • 实现IoC控制反转思想的一种技术手段.
  • 创建IoC容器
ApplicationContext context = new AnnotationConfigApplicationContext("包路径");
ApplicationContext context = new ClasspathXmlConfigApplicationContext("xxx.xml");

3.3 依赖注入DI

DI (Dependency Injection):依赖注入,依赖注入实现了控制反转的思想,是指Spring创建对象的过程中,将对象依赖属性通过配置进行注入

所以 IoC 是一种控制反转的思想,而依赖注入 DI 是对 IoC 的一种具体实现

Bean管理:指Bean对象的创建,以及Bean对象中属性的赋值

总结:IoC是控制反转思想, IoC容器和DI依赖注入是实现IoC控制反转思想的两种技术手段.

4 IoC容器实现

Spring中的IoC容器就是IoC思想的一个落地产品实现。IoC容器中管理的组件也叫做bean。在创建bean之前,首先需要创建IoC容器,Spring提供了IoC容器的两种实现方式

  • BeanFactory: 这是IoC容器的基本实现,是Spring内部使用的接口,面向Spring本身,不提供给开发人员使用。
  • ApplicationContext :BeanFactory的子接口,提供了更多高级特性,面向Spring的使用者,几乎所有场合都使用 ApplicationContext,而不使用底层的BeanFactory。

  • ApplicationContext的主要实现类

类型

说明

AnnotationConfigApplicationContext

使用注解方式构建IoC容器

ClassPathXmlApplicationContext

使用XML配置文件方式构建Spring IoC容器

AnnotationConfigApplicationContext 常用

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

/**
 * @author lhd
 * date 2024/4/16
 * @apiNote
 */
public class TestProduct {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext("cn.lhd.spring.eg1");
        Product product = context.getBean(Product.class);
        System.out.println("product = " + product);
    }
}
ClassPathXmlApplicationContext 比较繁琐


import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author lhd
 * @date 2024/4/16
 * @apiNote
 */
public class UserXml {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        UserXml userXml = context.getBean(UserXml.class);
        userXml.run();
    }

    public static void run(){
        System.out.println("每天飞飞飞");
    }
}

<?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="user" class="cn.tedu.spring.beanxml.UserXml">
    </bean>
</beans>
IoC具体实现流程

1.扫描包路径找到带有@Component注解的类

2.存放Spring Bean对象

3.Bean对象默认名称:

        类名首字母小写,类名为UserDao 创建的Bean对象为 userDao

5 依赖注入DI

DI (Dependency Injection):依赖注入,依赖注入实现了控制反转的思想;

是指Spring创建对象的过程中,将对象依赖属性通过配置进行注入。

所以 IoC 是一种控制反转的思想,而依赖注入 DI 是对 IoC 的一种具体实现。

6 注解管理Bean

6.1 Bean对象定义及获取

在Spring框架规范中,所有由spring管理的对象都称之为Bean对象。

6.1.1 Bean对象定义

Spring提供了以下多个注解,这些注解可以直接标注在java类上,将它们定义成Spring Bean。

注解

说明

@Component

该注解用于描述Spring中的Bean,它是一个泛化的概念,仅仅标识容器中的一个组件(Bean),并且可以作用在任何层次,例如Service层、Dao层等,使用时只需将该注解标注在相应的类上即可。

@Repository

该注解用于数据访问层(Dao层)的类标识为Spring中的Bean,功能与@Component相同。

@Service

该注解通常作用在业务层(Service层),用于将业务层的类标识为Spring中的Bean,其功能与@Component相同。

@Controller

该注解通常作用在控制层(如SpringMVC的Controller),用于将控制层的类标识为Spring中的Bean,其功能与@Component相同。

6.1.2 Bean对象获取

通过 ApplicationContext 对象获取:调用 ApplicationContext 对象的 getBean() 方法,传入对应类的类型即可获取该 Bean 对象,示例:

ApplicationContext context = new AnnotationConfigApplicationContext("包扫描路径");
User user = context.getBean(类名.class);
6.1.3 应用分析
  • 第1步:在bean包下创建类:UserDao
package com.lhd.spring.di;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;

/**
 * 依赖注入示例
 * Controller: 控制器层
 * Service: 业务层
 * Repository: 数据层
 * Component: 不分层,标识为普通的Spring Bean组件
 */
@Component
public class UserDao {
    private String username;
    private String password;
    private String url;

}

· 第2步:在bean包下创建测试类:TestUserDao

package com.lhd.spring.di;

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

/**
 * 测试类
 */
public class TestUserDao {
    public static void main(String[] args) {
        /*
            1.基于注解方式创建IoC容器;
            2.获取Spring Bean对象;
            3.打印测试.
         */
        ApplicationContext context = new AnnotationConfigApplicationContext("com.lhd.spring.di");
        UserService userService = context.getBean(UserService.class);
        System.out.println("userService = " + userService);
    }
}

说明:将java类中的 @Repository注解 替换为 @Component注解、@Service注解、@Controller注解 都是可以的。

6.2 依赖注入DI

  • @Value
    • 2个位置:添加在属性上, set方法上;
    • 简单类型依赖注入.
    • 补充:可以注入spring.properties配置文件中的值
  • @Autowired
    • 3个位置:添加在属性上,添加在set方法上,构造方法上;
    • 对象类型或者接口类型依赖注入,默认根据 类型 进行装配;
  • @Qualifier
    • 根据 Spring Bean对象的名称进行注入, 经常配合 @Autowired 注解一起使用;        
    • 当一个接口有多个实现类时, 可以通过 @Qualifier(value="Bean对象名称") 指定需要注入的对象;
  • @Resource
  • 接口和对象类型属性值注入;
  • @Autowired注解和@Resource注解的区别
    • @Resource 注解是JDK的标准注解, @Autowired 注解是 Spring Framework 框架的注
    • 装配规则不同
    • @Autowired 注解默认根据类型进行装配, 如果需要根据名称进行装配, 结合 @Qualifier 注解;
    • @Resource 注解
      • 默认根据名称进行装配[@Resource 注解的 name 参数];
      • 如果未指定 name 参数, 则以 属性名作为Spring Bean对象名称进行装配;
      • 如果属性名和Bean对象名称也不一致, 则根据类型进行装配.

详细区别可参考另外一篇文章 http://t.csdnimg.cn/Sc4bN 

6.2.1 @Value注解

@Value 注入是将属性值直接注入到 bean 中,主要用于注入一些简单类型的属性(如字符串、基本类型等);

使用时需要注意属性的类型和格式,否则会导致注入失败。

示例:在UserDao中进行属性值注入

  • 第1步:在UserDao的属性中通过@Value注解注入属性值

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
 * @author lhd
 * @date 2024/4/16
 * 依赖注入演示
 * @apiNote
 */
@Component
public class UserDao {
    //@Value注解 :基本类型的依赖注入
    @Value("root")
    private String name;
    @Value("123456")
    private String password;
    @Value("jdbc:mysql://localhost:3306/db")
    private String url;

    public void run(){
        System.out.println("开始学习spring啦");
    }
    @Override
    public String toString() {
        return "UserDao{" +
        "name='" + name + '\'' +
        ", password='" + password + '\'' +
        ", url='" + url + '\'' +
        '}';
    }
}
6.2.2 @Autowired注解

@Autowired 注入是将对象注入到 bean 中,并且在注入对象时会根据依赖注入容器中 bean的类型 进行匹配。

如果容器中有多个类型匹配的bean存在,则会抛出异常。

因此,@Autowired注入常用于注入复杂对象、接口类型的属性或其他bean实例。

6.2.2.1 根据类型注入

定义UserService类,并注入UserDao对象

  • 第1步:在包bean下创建 UserService 类


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

/**
 * @author lhd
 * @date 2024/4/16
 * @apiNote
 */
@Service
public class UserService {
    /*
        Autowired 注解:
            1.自动装配注解 注入对象类型和接口类型的数据
            2.添加位置
                1. 属性上
                2. set方法上
                3. 构造方法上

     */
    //    @Autowired
    // 放在属性上
    private UserDao userDao;


    //@Autowired
    //放在构造器上
    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }
    @Autowired
    // 放在set方法上
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public UserService() {
    }


    public void run()
    {
        userDao.run();
    }

    @Override
    public String toString() {
        return "UserService{" +
        "userDao=" + userDao +
        '}';
    }
}

· 第2步:调整测试方法



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

/**
 * @author lhd
 * @date 2024/4/16
 * @apiNote
 */
public class TestUserService {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext("com.lhd.spring.di");
        UserService userService = context.getBean(UserService.class);
        System.out.println(userService);
        userService.run();
    }
}

6.2.2.2 根据接口类型注入

进行依赖注入时,如果指定的是接口 Interface,Spring 框架会自动找到该接口对应的实现类并创建 bean 对象注入吗?

在 com.lhd.spring 下创建子包 auto

  • 第1步:创建接口 Cache
package com.lhd.spring.auto;

public interface Cache {
}
  • 第2步:创建该接口实现类 CacheImpl1 ,并添加 @Component注解

import org.springframework.stereotype.Component;

@Component
public class CacheImpl1 implements Cache{
}
  • 第3步:创建类 UserCache ,并注入 CacheImpl1对象
@Component
public class UserCache {
    @Autowired
    private Cache cache;
}
  • 第4步:创建测试类 TestUserCache 进行测试
public class TestUserCache {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext("com.lhd.spring.auto");
        Cache bean = context.getBean(UserCache.class);
        System.out.println("bean = " + bean);
    }
}

总结: 当一个接口有一个唯一的实现类时,Spring框架会通过接口找到该接口对应的实现类,并进行bean对象的创建以及DI注入操作。

问题:那么如果一个接口有多个实现类,Spring框架会创建对应的 java bean 对象吗?

  • 第1步:创建 Cache 接口的实现类 CacheImpl2
@Component
public class CacheImpl2 implements Cache{
}
  • 第2步:执行测试方法,确认是否成功

解决方案:

注入时候 使用@Qualifier注解 指定注入实现类的名称

  • 总结: 当一个接口有多个实现类时,Spring无法确定注入哪个实现类对象,因此会报错,可以结合 @Qualifier注解 来解决这个问题。

6.2.3 @Qualifier注解

@Qualifier注解是用于限定一个接口有多个实现类时,根据指定的限定条件来选择具体的实现类的注解;

当Spring容器中存在多个实现同一接口的bean时,在注入时,由于不能确定注入哪一个实现类,就需要通过@Qualifier注解来明确指定要注入的bean的名称

@Qualifier注解演示

  • 第1步:在 UserCache 中添加 @Autowired注解 和 @Qualifier注解
@Component
public class UserCache {
    @Autowired
    @Qualifier("aaa")
    private Cache cache;
}
  • 第2步:在 CacheImpl1 实现类中 @Component注解 中添加组件名
@Component("aaa")
public class CacheImpl1 implements Cache{
}
  • 第3步:执行测试方法进行测试

6.2.4 @Resource注解(了解)

@Resource 注解是 JavaEE 提供的注解之一,也支持在 Spring Framework 中使用。在 Spring 中,它可以用来注入 Bean 实例,与@Autowired注解的作用类似,但其也有自己的一些特点。

  • @Resource 注解是JDK扩展包中的,也就是说属于JDK的一部分。所以该解释是标准注解,而 @Autowired 注解是 Spring 框架自己的。
  • 装配规则
    •  @Resource 注解默认根据名称装配 byName;
    • 当未指定 name 时,则使用属性名作为 name 进行装配;
    • 如果通过 name 也未找到,则会自动启动通过类型 byType 装配。
  • 而@Autowired注解默认根据类型装配byType,如果想根据名称匹配,需要配合@Qualifier注解一起使用。示例

  • 包名:resource
  • 接口:ResMapper
/**
 * @author lhd
 * @date 2024/4/16
 * @apiNote
 */
public interface ResMapper {
}
  • 实现类1:ResMapperImpl1
import org.springframework.stereotype.Service;

/**
 * @author lhd
 * @date 2024/4/16
 * @apiNote
 */
@Service
public class ResMapperImpl1 implements ResMapper {
}
  • 实现类2:ResMapperImpl2
import org.springframework.stereotype.Service;

/**
 * @author lhd
 * @date 2024/4/16
 * @apiNote
 */
@Service
public class ResMapperImpl2 implements ResMapper{
}
  • DI注入类:WeiboMapper

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * @author lhd
 * @date 2024/4/16
 * @apiNote
 */
@Component
public class WeiboMapper {
    /*
    Resource 注解 对象或接口类型的依赖注入,装配规则如下:
        1. 根据Spring Bean对象的名称进行装配[通过name参数]
        2. 如果未指定Bean对象名称[name参数] 把属性名作为 Bean对象名称进行装配 -->属性名 resMapperImpl1
        3. 如果属性名 和 Bean对象名称也不一致,则根据类型进行装配 -->类型 ResMapper
        4. 如果一个接口有多个实现类,则根据类型装配式也会抛出异常
  */
    //     @Resource(name = "resMapperImpl1")
    @Resource
    private ResMapper resMapperImpl1;

}
  • 总结
    • 指定 @Resource 中的 name,则根据名称装配;
    • 未指定 name 时,则根据属性名装配;
    • 未指定 name,属性名也不一致,则根据类型装配.
  • 测试类:TestWeiboMapper


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

/**
 * @author lhd
 * @date 2024/4/16
 * @apiNote
 */
public class TestWeiboMapper {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext("cn.tedu.spring.resource");
        WeiboMapper weiboMapper = context.getBean(WeiboMapper.class);
        System.out.println(weiboMapper);
    }
}

6.3 Bean作用域

6.3.1 说明

在Spring框架中,Bean是按照作用域来创建的,常见的作用域有两种:Singleton 和 Prototype。其中,Singleton (单例)是指整个应用中只有一个实例,并在第一次请求时创建实例。而 Prototype (多例)是指每次请求都会创建一个新的实例并返回,每个实例之间是相互独立的。可以通过 @Scope 注解来指定,默认是单实例。

6.3.2 单实例与多实例
  • 单实例单实例(Singleton)是指某个类只能创建唯一的一个实例对象,并且该类提供一个全局的访问点(静态方法)来让外界获取这个实例,常常用在那些只需要一个实例来处理所有任务的场景下,例如数据库连接。
  • 多实例多实例(Multiple Instance)则是指可以在同一个类的定义下,创建多个实例对象。每个对象都是相互独立的,有自己的状态和行为;常常用于需要同时处理多个任务的场景。

在Spring中可以通过 @Scope 注解来指定bean的作用域范围,具体如下

取值

含义

@Scope("singleton")(默认

在IoC容器中,这个bean的对象为单实例

@Scope("prototype")

这个bean在IoC容器中有多个实例

6.3.3 应用分析
  • 第1步:在 cn.tedu.spring 下创建子包 scope ,并创建类 DBConnect
@Scope(value = "singleton")
@Component
public class DBConnect {
}
  • 第2步:创建测试类进行测试
public class TestDBConnect {
    @Test
    public void testScope(){
        ApplicationContext context = new AnnotationConfigApplicationContext("cn.tedu.spring.scope");
        // 第一次获取
        DBConnect dbConnect1 = context.getBean(DBConnect.class);
        System.out.println(dbConnect1);
        // 第二次获取
        DBConnect dbConnect2 = context.getBean(DBConnect.class);
        System.out.println(dbConnect2);
    }
}
  • 总结
    • 当为单例模式 singleton 时,多次获取bean实例的地址是相同的

​       -  当为多例模式 prototype 时,多次获取bean实例的地址是不同的

单例模式适用于需要共享数据并且需要避免重复创建实例的情况。而多例模式适用于需要动态地创建对象并提供独立实例的情况。

6.4 Bean生命周期

6.4.1 说明

程序中的每个对象都有生命周期,对象的创建、初始化、应用、销毁的整个过程称之为对象的生命周期;

在对象创建以后需要初始化,应用完成以后需要销毁时执行的一些方法,可以称之为是生命周期方法;

在spring中,可以通过 @PostConstruct 和 @PreDestroy 注解实现 bean对象 生命周期的初始化和销毁时的方法。

  • @PostConstruct 注解生命周期初始化方法,在对象构建以后执行。
  • @PreDestroy 注解生命周期销毁方法,比如此对象存储到了spring容器,那这个对象在spring容器移除之前会先执行这个生命周期的销毁方法(注:prototype作用域对象不执行此方法)。
6.4.2 完整生命周期
  1. 实例化阶段(bean对象创建)在这个阶段中,IoC容器会创建一个Bean的实例,并为其分配空间。这个过程可以通过 构造方法 完成。
  2. 属性赋值阶段在实例化完Bean之后,容器会把Bean中的属性值注入到Bean中,这个过程可以通过 set方法 完成。
  3. 初始化阶段(bean对象初始化)在属性注入完成后,容器会对Bean进行一些初始化操作;
  4. 使用阶段初始化完成后,Bean就可以被容器使用了
  5. 销毁阶段容器在关闭时会对所有的Bean进行销毁操作,释放资源。
6.4.3 生命周期验证
  • 第1步:DBConnect 类中打印生命周期过程


/**
 * @author lhd
 * @date 2024/4/17
 * @apiNote
 */

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

/**
 * Spring Bean 生命周期
 * 实例化[构造方法]
 * 属性赋值[set方法]
 * 初始化[生命周期初始化方法PostConstruct]
 * 使用阶段
 * 销毁阶段[生命周期的销毁方法PreDestroy]
 */
@Component
public class Life {
    private String mean;

    public Life() {
        System.out.println("1.调用无参构造进行实例化");
    }

    @Value("世界哪么大我想去看看")
    public void setMean(String mean) {
        this.mean = mean;
        System.out.println("2.调用set方法进行属性赋值");
    }

    @PostConstruct
    public void init() {
        System.out.println("3.调用PostConstruct进行初始化");
    }

    @PreDestroy
    public void destroy() {
        System.out.println("5.执行生命周期的销毁方法");
    }

}

· 第2步:测试类中打印生命周期过程



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

/**
 * @author lhd
 * @date 2024/4/17
 * @apiNote
 */
public class TestLife {
    public static void main(String[] args) {

        //        ApplicationContext context = new AnnotationConfigApplicationContext
        //                ("cn.tedu.spring.lifecycle");
        //创建IoC容器 AnnotationConfigApplicationContext 便于close手动关闭
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext
        ("cn.tedu.spring.lifecycle");
        Life life = context.getBean(Life.class);
        //使用阶段
        System.out.println(
            "4.开发者使用阶段"
        );
        //关闭IoC容器 AnnotationConfigApplicationContext
        context.close();
        /**
         * 运行结果:
         * 1.调用无参构造进行实例化
         * 2.调用set方法进行属性赋值
         * 3.调用PostConstruct进行初始化
         * 4.开发者使用阶段
         * 5.执行生命周期的销毁方法
         */
    }
}

· 第3步:生命周期结果验证

6.4.4 生命周期扩展

Bean初始化和销毁方法可以在Bean生命周期的特定时机执行自定义逻辑,方便地对Bean进行管理和配置。

  • 初始化常见应用场景
    • 创建数据库连接
    • 加载资源文件
    • 进行数据校验
  • 销毁常见应用场景
    • 断开数据库连接
    • 保存数据
    • 释放占用的资源

6.5 引用外部属性文件

6.5.1 说明

实际开发中,很多情况下我们需要对一些变量或属性进行动态配置,而这些配置可能不应该硬编码到我们的代码中,因为这样会降低代码的可读性和可维护性。

我们可以将这些配置放到外部属性文件中,比如database.properties文件,然后在代码中引用这些属性值,例如jdbc.url和jdbc.username等。这样,我们在需要修改这些属性值时,只需要修改属性文件,而不需要修改代码,这样修改起来更加方便和安全。

而且,通过将应用程序特定的属性值放在属性文件中,我们还可以将应用程序的配置和代码逻辑进行分离,这可以使得我们的代码更加通用、灵活。

6.5.2 使用流程
  • 第1步:创建外部属性文件(在 resources 目录下创建文件,命名为:"xxx.properties");
  • 第2步:引入外部属性文件(使用 @PropertySource("classpath:外部属性文件名") 注解);
  • 第3步:获取外部属性文件中的变量值 (使用 ${变量名} 方式);
  • 第4步:进行属性值注入.
6.5.3 应用分析
  • 第1步:在 resources 目录下创建文件 :database.properties
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.url=jdbc://mysql://localhost:3306/test
  • 第2步:工程目录下创建子包 file 并创建类 Database
    • 通过 @PropertySource 注解引入外部文件
    • 通过 ${变量名} 获取属性值
    • 通过 @Value() 注解进行属性值注入
@Component
@PropertySource("classpath:database.properties")
public class Database {
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.user}")
    private String username;
    @Value("${jdbc.password}")
    private String password;

    @Override
    public String toString() {
        return "Database{" +
        "url='" + url + '\'' +
        ", username='" + username + '\'' +
        ", password='" + password + '\'' +
        '}';
    }
}
  • 第3步:创建测试类 TestDatabase 进行测试
public class TestDatabase {
    @Test
    public void testFile(){
        ApplicationContext context = new AnnotationConfigApplicationContext("cn.tedu.spring.file");
        Database database = context.getBean(Database.class);
        System.out.println(database);
    }
}

6.6 自动扫描配置

6.6.1 说明

自动扫描配置是 Spring 框架提供的一种基于注解(Annotation)的配置方式,用于自动发现和注册 Spring 容器中的组件。当我们使用自动扫描配置的时候,只需要在需要被 Spring 管理的组件(比如 Service、Controller、Repository 等)上添加对应的注解,Spring 就会自动地将这些组件注册到容器中,从而可以在其它组件中使用它们。

在 Spring 中,通过 @ComponentScan 注解来实现自动扫描配置。

@ComponentScan 注解用于指定要扫描的包或类。

Spring 会在指定的包及其子包下扫描所有添加 @Component(或 @Service、@Controller、@Repository 等)注解的类,把这些类注册为 Spring Bean,并纳入 Spring 容器进行管理。

6.6.2 使用示例
@Configuration
@ComponentScan("com.lhd.srping")
public class AppConfig {

}

在此示例中,

  • @Configuration 注解表示将类 AppConfig 标识为一个 Spring 配置类,Spring 会来加载此类,并且读取其中的配置。
  • @ComponentScan 注解用于指定扫描的包路径 com.example.app。Spring 会自动在 cn.tedu.spring 包及其子包下扫描所有被 @Component 等注解标注的类,并将这些类注册为 Spring Bean。
6.6.3 应用分析
  • 第1步: 工程下创建包 config,并在此包下创建类 SpringConfig
/**
 * @Configuration注解: 将类 SpringConfig 标识为一个 Spring 配置类
 */
/**
 * @ComponentScan注解:指定要扫描的包路径 cn.tedu.spring,包含该包下的子包
 */
@Configuration
@ComponentScan("com.lhd.spring")
public class SpringConfig {

}
  • 第2步:创建测试类 TestScan 进行测试
public class TestScan {
    @Test
    public void testScan(){
        // 指定配置类:SpringConfig
        ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        // 获取bean对象
        Database database = context.getBean(Database.class);
        System.out.println(database);
        // 获取bean对象
        UserCache userCache = context.getBean(UserCache.class);
        System.out.println("userCache = " + userCache);
    }
}


常见异常

1 NoSuchBeanDefinitionException
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'cn.tedu.spring.bean.UserService' available ...
  • 解决方案
    • 查看该类上是否添加标识为 Spring 组件的注解;
    • 检查相关注解 Spring Bean 对象的名称是否一致;
2 NoUniqueBeanDefinitionException
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'cn.tedu.spring.auto.Cache' available: expected single matching bean but found 2: cacheImpl1,cacheImpl2
  • 解决方案:
    • 一个接口有多个实现类,通过 @Qualifier 或者 @Resource 注解指定唯一的 Bean 对象的名称.
  • 23
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值