SpringBootWeb 篇-深入了解 Bean 的管理与 SpringBoot 起步依赖、SpringBoot 自动配置原理(源码追踪:jar 包配置类如何加载到 IOC 容器中?)
小扳 2024-08-14 10:33:04 阅读 95
🔥博客主页: 【小扳_-CSDN博客】
❤感谢大家点赞👍收藏⭐评论✍
文章目录
1.0 Bean 的管理
1.1 Bean 的获取
1.2 Bean 的作用域
1.3 第三方 Bean
1.3.1 @Bean 与 @Component 的区别
2.0 SpringBoot 原理
2.1 SpringBoot 起步依赖原理
2.2 SpringBoot 自动配置原理
2.2.1 依赖 jar 包中的配置类的 bean 对象是如何直接加载到 IOC 容器中的?
2.2.2 使用 @ComponentScan 组件扫描
2.2.3 使用 @Import 注解导入
2.3 SpringBoot 自动配置原理 - 源码跟踪
2.4 SpringBoot 自动配置原理 - 按条件装配(@Conditional 注解)
1.0 Bean 的管理
在 Spring 框架中,Bean 的管理是通过 Spring IOC 容器来实现的。Spring的IOC 容器负责创建、配置、管理 Bean 实例,以及管理 Bean 之间的依赖关系。Spring IOC 容器通过反射机制来实例化 Bean,并通过配置文件或注解来指定 Bean 的属性值和依赖关系。
一般通过常见的基于注解的配置方式来完成 Bean 的管理。通过在 Bean 类上添加 @Component、@Service、@Repository 等注解,告诉 Spring 容器将该类注册为 Bean,然后在配置类中通过 @ComponentScan 来扫描并加载 Bean 。
最后通过 @Autowired 注解来实现注入。
代码演示:
IOC 容器中的 Bean 对象:
<code>import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.stereotype.Component;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Component
public class Student {
private String name;
private String gender;
private int age;
}
使用 DI 对象注入:
import junit.framework.TestCase;
import org.example.pojo.Student;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class AppTest extends TestCase {
@Autowired
Student student;
@Test
public void test1(){
student.setName("Tom");
student.setAge(21);
student.setGender("男");
System.out.println(student);
}
}
1.1 Bean 的获取
除了通过 @Autowired 注解进行对象注入以外,还可以直接通过从 IOC 容器中进行手动获取 Bean 对象。
默认情况下,Spring 项目启动时,会把 bean 都创建好放在 IOC 容器中,如果想要主动获取这些 bean ,可以通过如下方式:
1)根据 name 获取 bean:Object getBean(String name)
2)根据类型获取 bean:<T> getBean(Class<T> requiredType)
3)根据 name 或者 bean(带类型转换):<T> T getBean(Spring name,Class<T> requiredType)
代码演示:
1)首先获取到容器对象,通过 @Autowired 注解方式将 ApplicationContext 类型的 Bean 对象注入到变量中。
2)再通过以上三种方式来获取指定的 bean 对象
<code> @Autowired
private ApplicationContext applicationContext;
@Test
public void test2(){
//根据name来获取bean对象
Student student1 = (Student) applicationContext.getBean("student");
System.out.println(student1);
//根据类型来获取bean对象
Student student2 = applicationContext.getBean(Student.class);
System.out.println(student2);
//根据name和类型来获取bean对象
Student student3 = applicationContext.getBean("student",Student.class);
System.out.println(student3);
}
运行结果:
说明了 Student 类型的 Bean 对象在 IOC 容器中有且仅有一份。
1.2 Bean 的作用域
每个 Spring 容器只会创建一个 Bean 实例,并且共享给所有依赖它的组件。这是 Spring 默认的作用域,通常适用于状态无关且线程安全的 Bean 。
常见的作用域:
1)singleton:容器内同名称的 bean 只有一个实例(默认)。
2)prototype:每次使用该 bean 时会创建新的实例(非单例)。
可以通过 @Scope 注解来进行配置作用域。
代码演示:
<code>import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@AllArgsConstructor
@Component
@Scope("singleton")
public class Student {
private String name;
private String gender;
private int age;
public Student() {
System.out.println("正在创建 IOC 容器对象");
}
}
@Autowired
ApplicationContext applicationContext;
@Test
public void test3(){
for (int i = 0; i < 10; i++) {
Student student = applicationContext.getBean(Student.class);
System.out.println(student);
}
}
用 @Scope 注解来配置在 IOC 容器中只有一个 bean 对象。接着在该 Student 类中额外设置了一个无参构造器,来查看 IOC 容器中的 bean 对象什么时候会自动创建 Student 对象且将对象放入 IOC 容器中。通过 for 循环,观察从 IOC 容器中取出来的 bean 对象是否时同一个对象。
运行结果:
默认在项目启动的时候,就会自动创建好对象,且将对象放到 IOC 容器中。
从这里很容易就可以看出来,通过 @Scope 注解设置 "singleton" 时,从 IOC 容器中获取的 bean 对象都是同一个对象。
接着用 @Scope 注解属性设置成 "prototype" 。
代码演示:
<code>import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@AllArgsConstructor
@Component
@Scope("prototype")
public class Student {
private String name;
private String gender;
private int age;
public Student() {
System.out.println("正在创建 IOC 容器对象");
}
}
@Autowired
ApplicationContext applicationContext;
@Test
public void test3(){
for (int i = 0; i < 10; i++) {
Student student = applicationContext.getBean(Student.class);
System.out.println(student);
}
}
运行结果:
每次获取 bean 对象时,都会先创建一个 Student 类型的对象且将放到 IOC 容器中,因此每一个获取的 bean 对象都不是同一个。
1.3 第三方 Bean
如果要管理的 bean 对象来自于第三方(不是自定义的),是无法用 @Component 及衍生注解声明 bean 的,就需要用到 @Bean 注解。
如果要将 SAXReader 对象交给 Spring IOC 容器管理,可以通过 @Bean 注解来手动配置。若要管理的第三方 bean 对象,建议对这些 bean 进行集中分类配置,可以通过 @Configuration 注解声明一个配置类。
1)先创建一个配置类,在创建完类之后,在该类加上一个 @Configuration 注解,这样就声明了一个配置类了。
<code>import org.springframework.context.annotation.Configuration;
@Configuration
public class Config {
}
2)创建有 SAXReader 返回值的方法,且在该方法上加上 @Bean 注解。
import org.dom4j.io.SAXReader;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class Config {
@Bean
public SAXReader saxReader(){
return new SAXReader();
}
}
将当前方法的返回值对象交给 IOC 容器管理,成为 IOC 容器 bean 。
代码演示:
@Autowired
ApplicationContext applicationContext;
@Test
public void test3(){
for (int i = 0; i < 10; i++) {
Student student = applicationContext.getBean(Student.class);
System.out.println(student);
}
}
运行结果:
通过 @Bean 注解来获取到方法的返回值,再将返回值放到 IOC 容器中,这样就完成了第三方 bean 对象正常的获取了。
1.3.1 @Bean 与 @Component 的区别
@Bean 注解通常用于配置类中的方法上,表示将该方法返回的对象添加到 Spring 的 IOC 容器中,用来自定义 bean 对象的创建方式,灵活性更高。
@Component 注解通常用于类级别上,表示将该类作为一个组件(bean)放入 IOC 容器中管理,Spring 会自动扫描并实例化这些组件。
因此,@Bean 是用于方法级别的注解,通过方法返回值来手动配置 bean 对象;而 @Component 是用于类级别的注解,Spring 会自动扫描并实例化被标记的类。两者可以互补使用,让 Spring 的 IOC 容器管理更加灵活和高效。
2.0 SpringBoot 原理
Spring Boot 是针对 Spring 框架的一种简化搭建方式,它可以让开发人员更快速地创建基于 Spring 的应用程序。它基于 Spring 框架,提供了一套开箱即用的配置,使得开发者只需少量的配置即可快速构建和部署应用程序。
介绍 SpringBoot 原理主要包含:起步依赖、自动配置。
2.1 SpringBoot 起步依赖原理
是 Spring Boot 中一个非常有用的功能,它主要用于简化和管理项目中的依赖关系。起步依赖本质上是一组 Maven 依赖的集合,而无需手动配置每个依赖项的版本和依赖关系。
起步依赖通常以 spring-boot-starter-xxx 的形式命名,其中的 “xxx” 表示具体的功能或框架。Spring Boot 提供了一系列预定义的起步依赖,如 spring-boot-starter-web、spring-boot-starter-data-jpa、spring-boot-starter-security 等,每个起步依赖都包含了各种对应功能所需的依赖关系,开发者只需引入相应的起步依赖,然后设置少量配置即可快速启动和运行项目。
起步依赖将一组相关的依赖打包到一个依赖中,减少了检索和管理多个依赖项的复杂性。
简单来说,假设:A 依赖于 B ,B 依赖于 C ,通过依赖传递只导入 A 依赖之后,B、C 也就引入进来了。
这种自动处理依赖传递的机制有助于简化依赖管理,并确保整个依赖链的所有相关依赖都能被正确地加载和使用,而不需要手动处理每个依赖。
也就是说,因为有了依赖传递的机制,所以才有了起步依赖功能。
2.2 SpringBoot 自动配置原理
自动配置(Auto-Configuration)是 Spring Boot 中一个重要的特性,通过自动配置,Spring Boot 可以根据项目中的依赖和配置来自动配置应用程序的功能。
SpringBoot 的自动配置就是当 Spring 容器启动后,一些配置类、bean 对象就自动存入到 IOC 容器中,不需要我们手动声明,从而简化了开发,省去了繁琐的配置操作。
2.2.1 依赖 jar 包中的配置类的 bean 对象是如何直接加载到 IOC 容器中的?
当引入一个依赖后,依赖的 jar 包中可能包含一些配置类,这些配置类中定义了一些 Bean 对象。这些 Bean 对象是如何加载到 Spring 的 IOC 容器中的呢?
在 Spring Boot 中,这是通过组件扫描和 @Import 注解来实现的。
2.2.2 使用 @ComponentScan 组件扫描
当使用 @ComponentScan 进行组件扫描时,Spring 容器会扫描指定包及其子包中的所有组件,包括配置类。如果配置类中使用了 @Configuration 注解,并且在配置类中定义了 @Bean 注解的方法用于创建 bean 对象,这些 bean 对象也会被加载到 Spring 的 IOC 容器中。
举个例子:
假设有一个jar包中的配置类如下所示:
<code>package com.example.jarpackage.config;
@Configuration
public class JarConfig {
@Bean
public ExampleBean exampleBean() {
return new ExampleBean();
}
}
在主应用程序的配置类中,通过 @ComponentScan 指定要扫描的包,包括 jar 包中的配置类所在的包路径:
package com.example.demo;
@Configuration
@ComponentScan(basePackages = {"com.example.jarpackage.config"})
public class AppConfig {
}
在上面的示例中,通过 @ComponentScan 注解指定要扫描的包是 "com.example.jarpackage.config" ,这样 Spring 容器将会扫描到 jar 包中的配置类 JarConfig ,并且也会加载 JarConfig 类中通过 @Bean 注解定义的 bean 对象到 IOC 容器中。
因此,当使用 @ComponentScan 进行组件扫描时,Spring 会将 jar 包中的配置类的 bean 对象直接加载到 IOC 容器中,使得这些 bean 对象可以被应用程序中其他组件注入和使用。
2.2.3 使用 @Import 注解导入
在启动类中,使用 @Import 导入的类会被 Spring 加载到 IOC 容器中,导入形式主要有以下几种:
1)导入普通类(无论有无 @Component 注解的类)
如果使用 @Import 导入的类是普通类,即没有任何特殊注解修饰,Spring 会将这些类直接加载到 IOC 容器中作为 bean 。这些类将在 Spring 应用程序中可用。
2)导入配置类(@Configuration 注解的类)
如果使用 @Import 导入的类是一个带有 @Configuration 注解的配置类,Spring 会将该配置类作为一个配置文件加载,其中定义的 @Bean 方法将被调用以创建 bean,并将这些 bean 加载到 IOC 容器中。
3)导入 ImportSelector 接口实现类
如果使用 @Import 导入的类是一个实现 ImportSelector 接口的类,Spring 会调用 ImportSelector 接口的实现类,根据实现类的逻辑来动态选择性地导入其他类或配置。
实现了 ImportSelector 接口,重写 selectImports() 方法,方法的返回值是一个字符串数组。简单来说,需要导入的配置类或者普通类的类名添加到该字符串数组中,接着通过 @Import 注解的形式将该实现 ImportSelector 的类配置。最终 SpringBoot 就会自动配置加载配置类或者普通类,将其对象放到 IOC 容器中,成为 Bean 对象。
<code>@Configuration
@Import(MyImportSelector.class)
public class AppConfig {
}
4)@EnableXXXX注解,封装 @Import 注解
@EnableXXXX 注解通常用来封装 @Import 注解,并提供一种更加便捷的方式来启用特定功能或配置。通过 @EnableXXXX 注解,您可以一次性导入多个相关的配置类或组件,并将它们统一地配置到 Spring 应用程序中。
简单来说,在开发中,一些功能或配置可能需要引入多个相关的配置类或普通类,并将它们加载到 IOC 容器中作为 Bean 对象使用。为了简化配置和管理,可以由专门负责的人员将这些需要加载的类通过 @Import 注解配置在一个统一的配置类中,然后由应用程序的开发者只需要在启动类中加上 @EnableXXXX 注解即可激活这些配置。
2.3 SpringBoot 自动配置原理 - 源码跟踪
在 SpringBoot 项目中,跟踪依赖 jar 包中的配置类或者普通类中的 bean 对象是如何加载到 IOC 容器中的。
从启动类中的 @SpringBootApplication 注解进行跟踪分析。在 @SpringBootApplication 注解中有三个核心注解:
1)@SpringBootConfiguration 注解:该注解于 @Configuration 注解作用相同,用来声明当前也是一个配置类,之所以该注解于 @Configuration 注解作用相同,是因为该注解中封装了 @Configuration 注解,如图:
2)@ComponentScan 注解:组件扫描,默认扫描当前引导类所在包及其子包。
3)@EnableAutoConfiguration 注解:SpringBoot 实现自动化配置的核心注解。
该注解封装了 @Import 注解且 @Import 导入的类是一个实现 ImportSelector 接口的类。
实现了 selectImports() 方法,返回值是字符串数组类型的对象。该字符串数组封装的就是需要加载类的全类名,那么这些类就会自动的导入到 IOC 容器中。
接着往下跟踪:
这里出现了两个文件名:
1)META-INF/spring.factories
2)META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
以上两个文件中存放的都是需要加载的配置类。
在 SpringBoot 项目启动的时候,就会自动加载这两个文件,将该配置类的全类名都加载到字符串数组中,接着由 @Import 注解完成将 bean 对象进行注入到 IOC 容器中。最后,就可以从 IOC 容器中来获取这些 bean 对象了。
从 Maven 项目中找到 spring.factories 文件,查看文件中的内容:
打开之后:存放的都是类的全类名
从 Maven 项目中找到 spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件,查看文件中的内容:
同样,存放的都是类的全类名:
这些类都会读取出来,并且通过 @Import 注解会加载到 IOC 容器中,交给 IOC 容器进行管理。
通过类名找该类的源码进行查看,举个例子:
当前类就是一个配置类,方法返回的是一个对象且加上了 @Bean 注解进行声明 bean 对象。当 SpringBoot 项目启动时,就会自动扫描加载该文件中的配置类,通过 @Import 注解将方法中的返回值对象都添加到 IOC 容器中。最终,就可以在我们不用手动配置下,就可以直接使用这些 bean 对象。
2.4 SpringBoot 自动配置原理 - 按条件装配(@Conditional 注解)
按照一定的条件进行判断,在满足给定的条件后才会注册对应的 bean 对象到 Spring IOC 容器中。
常见的条件注解:
1)@ConditionalOnClass 注解:判断环境中是否有对应的字节码文件,才注册 bean 到 IOC 容器。
<code>@Configuration
@ConditionalOnClass(RedisOperations.class)
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate() {
// 创建 RedisTemplate 对象
}
}
例如:假设我们的应用需要使用 Redis 缓存,我们可以使用 @ConditionalOnClass 注解来判断当前应用的 classpath 下是否有 Redis 相关的类,如果存在,就会注册一个 RedisTemplate 的 Bean 到 IOC 容器中。
2)@ConditionalOnMissingBean 注解:判断环境中没有对应的 bean ,才注册 bean 到 IOC 容器中。
@Configuration
public class CacheConfig {
@Bean
@ConditionalOnMissingBean(CacheManager.class)
public CacheManager cacheManager() {
// 创建自定义的 CacheManager 对象
}
}
假设我们需要在应用中使用自定义的 CacheManager,但是如果应用中已经有了 CacheManager,就不需要再创建一个新的了。我们可以使用 @ConditionalOnMissingBean 注解来判断当前应用上下文中是否已经有了 CacheManager,如果没有,就会创建一个自定义的 CacheManager Bean 。
3)@ConditionalOnProperty 注解:判断配置文件中有对应属性和值,才注册 bean 到 IOC 容器中。
@Configuration
@EnableJpaRepositories
@ConditionalOnProperty(name = "spring.datasource.url")
public class JpaConfig {
// 配置 JPA 数据源、事务管理等相关信息
}
假设我们需要在应用中使用 JPA,但是如果没有配置相关的数据库连接信息,就不需要创建 JPA 相关的 Bean 。我们可以使用 @ConditionalOnProperty 注解来判断配置文件中是否存在相关的属性值,如果存在,就会创建 JPA 相关的 Bean 。
上一篇: postman可以通的请求,前端通不了(前端添加Content-type,后端收不到请求)
下一篇: 初识Spring Web MVC
本文标签
SpringBootWeb 篇-深入了解 Bean 的管理与 SpringBoot 起步依赖、SpringBoot 自动配置原理(源码追踪:jar 包配置类如何加载到 IOC 容器中?)
声明
本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。