【SpringBoot】Bean作用域 && Bean生命周期 && SpringBoot自动配置操作 && 自动装配原理

文章目录

  • [Ⅰ. Bean的作用域](#Ⅰ. Bean的作用域)
  • [Ⅱ. Bean的生命周期](#Ⅱ. Bean的生命周期)
  • [Ⅲ. SpringBoot自动配置](#Ⅲ. SpringBoot自动配置)
    • [一、Spring 加载 Bean](#一、Spring 加载 Bean)
      • [1. 问题描述](#1. 问题描述)
      • [2. 原因分析](#2. 原因分析)
      • [3. 解决方案](#3. 解决方案)
        • [① `@ComponentScan`](#① @ComponentScan)
        • [② `@Import`](#② @Import)
    • 二、自动装配原理
      • [1. 源码阅读](#1. 源码阅读)
      • [2. `@EnableAutoConfiguration` 详解](#2. @EnableAutoConfiguration 详解)
        • [① `@Import({AutoConfigurationImportSelector.class})`](#① @Import({AutoConfigurationImportSelector.class}))
        • [② `@AutoConfigurationPackage`](#② @AutoConfigurationPackage)
      • [3. 总结](#3. 总结)

Ⅰ. Bean的作用域

一、概念

Spring IoCDI 部分学习了 Spring 是如何帮助我们管理对象的。

  1. 通过五大类注解 @Controller@Service@Repository@Component@Configuration 和方法注解 @Bean 来声 Bean 对象。
  2. 通过 ApplicationContext 或者 BeanFactory 来获取对象
  3. 通过 @AutowiredSetter 方法或者构造方法等来为应用程序注入所依赖的Bean对象

默认情况下,Spring 容器中的 bean 都是单例的,这种行为模式就称之为 Bean 的作用域。

二、Bean的作用域

名称 生命周期 作用粒度 底层对象 所属环境
singleton Spring 容器存活期间 容器级全局共享 Spring 容器 所有环境通用
prototype 每次获取新建实例 每次请求 Bean 时 Spring 容器 所有环境通用
request 一次 HTTP 请求 单次请求内共享 HttpServletRequest 仅 Web 环境
session 一次用户会话 单个用户会话级别 HttpSession 仅 Web 环境
application 整个 Web 应用 全局共享 ServletContext 仅 Web 环境
websocket 一次 WebSocket 连接 单连接 WebSocket 会话 仅 WebSocket

定义几个不同作用域的 Bean,需要搭配不同的注解,如下所示:

java 复制代码
@Component
public class DogBeanConfig {
    @Bean // 默认是单例singleton
    public Dog dog(){
        Dog dog = new Dog();
        dog.setName("旺旺");
        return dog;
    }
    
    @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
    public Dog singleDog(){
        Dog dog = new Dog();
        return dog;
    }
    
    @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public Dog prototypeDog(){
        Dog dog = new Dog();
        return dog;
    }
    
    @Bean
    @RequestScope
    public Dog requestDog() {
        Dog dog = new Dog();
        return dog;
    }
    
    @Bean
    @SessionScope
    public Dog sessionDog() {
        Dog dog = new Dog();
        return dog;
    }
    
    @Bean
    @ApplicationScope
    public Dog applicationDog() {
        Dog dog = new Dog();
        return dog;
    }
}

测试不同作用域的Bean取到的对象是否一样:

java 复制代码
@RestController
public class DogController {
    @Autowired
    private Dog singleDog;
    
    @Autowired
    private Dog prototypeDog;
    
    @Autowired
    private Dog requestDog;
    
    @Autowired
    private Dog sessionDog;
    
    @Autowired
    private ApplicationContext applicationContext;
    
    @RequestMapping("/single")
    public String single(){
        Dog contextDog = (Dog)applicationContext.getBean("singleDog");
        return "dog:"+singleDog.toString()+",contextDog:"+contextDog;
    }
    
    @RequestMapping("/prototype")
    public String prototype(){
        Dog contextDog = (Dog)applicationContext.getBean("prototypeDog");
        return "dog:"+prototypeDog.toString()+",contextDog:"+contextDog;
    }
    
    @RequestMapping("/request")
    public String request(){
        Dog contextDog = (Dog)applicationContext.getBean("requestDog");
        return "dog:"+requestDog.toString()+",contextDog:"+contextDog.toString();
    }
    
    @RequestMapping("/session")
    public String session(){
        Dog contextDog = (Dog)applicationContext.getBean("sessionDog");
        return "dog:"+sessionDog.toString()+",contextDog:"+contextDog.toString();
    }
    
    @RequestMapping("/application")
    public String application(){
        Dog contextDog = (Dog)applicationContext.getBean("applicationDog");
        return "dog:"+applicationDog.toString()+",contextDog:"+contextDog.toString();
    }
}

单例作用域:http://127.0.0.1:8080/single

多次访问,得到的都是同一个对象,并且 @AutowiredapplicationContext.getBean() 也是同一个对象。

多例作用域:http://127.0.0.1:8080/prototype

观察 ContextDog,每次获取的对象都不一样(而注入的对象 dog 在 Spring 容器启动时,就已经注入了,所以多次请求也不会发生变化)

请求作用域:http://127.0.0.1:8080/request

在一次请求中,@AutowiredapplicationContext.getBean() 也是同一个对象。

但是每次请求,都会重新创建对象

会话作用域:http://127.0.0.1:8080/session

在一个session中,多次请求,获取到的对象都是同一个。

换一个浏览器访问,发现会重新创建对象。(另一个Session)

Application作用域:http://127.0.0.1:8080/application

在一个应用中,多次访问都是同一个对象

singleton application
生命周期 Spring 容器级(随容器启动) ServletContext 级(Web 应用级别
管理者 Spring IoC 容器 Web 容器(Tomcat/Jetty)
存储位置 Spring 的单例缓存池(Map) ServletContext#setAttribute()
可访问范围 Spring 管理的类之间 整个 Web 应用中共享
是否依赖 Web 环境 ❌ 否(通用) ✅ 是(仅限 Web 项目)
被 Spring 管理? ✅ 是 ✅ 是,但底层基于 Web 容器
  1. Web 容器 (比如 Tomcat、Jetty)
    1. 是运行环境,负责管理和调度所有部署进去的 Web 应用。
    2. 它 "包含" 多个 Web 应用,每个 Web 应用是一个独立的运行单元
    3. Web 容器本身不直接管理 Spring 容器,但它提供了 Web 应用运行所需的基础设施(线程池、Servlet 容器、ServletContext 等)。
  2. Web 应用
    1. 是部署在 Web 容器中的一个具体应用(比如你的商城、博客系统)。
    2. 它包含了应用的代码、资源和配置。
    3. 在 Web 应用启动过程中,会创建和初始化 Spring 容器(WebApplicationContext)
    4. Web 应用通过配置(比如 web.xml 中的 ContextLoaderListener 或 Spring Boot 的启动类)来启动 Spring 容器。
  3. Spring 容器 ApplicationContext / WebApplicationContext
    1. 是 Web 应用中的一部分,专门负责管理 Bean、依赖注入、生命周期等。
    2. Spring 容器依赖 Web 应用提供的环境(比如可以通过 ServletContext 获得一些信息)。
    3. Spring 容器和 Web 应用相互配合:
      • Spring 管理业务逻辑和组件;
      • Web 应用提供 Web 运行时环境(HTTP 请求、Session 等);

🧩 类比一下:

  • Web 容器是大楼物业;
  • Web 应用是大楼里的租户办公室;
  • Spring 容器是办公室里的智能管理系统,帮租户高效管理员工(Bean)和资源。

💥通常情况下,一个 Web 应用 应该只拥有一个 Spring 容器 实例ApplicationContext ,并且 Spring 的底层设计就是这么保障的,以避免 Bean 冲突、上下文混乱等问题。

Ⅱ. Bean的生命周期

生命周期指的是一个对象从诞生到销毁的整个生命过程,这个过程就叫做一个对象的生命周期。

Bean的生命周期分为以下5个部分:

  1. 实例化 (为 Bean 分配内存空间,即 构造方法
  2. 属性赋值 (Bean 的注入 和装配,比如 @AutoWired
  3. 初始化
    1. 执行各种通知,如 BeanNameAwareBeanFactoryAwareApplicationContextAware 的接口方法。
    2. 执行初始化方法
      1. xml 定义 init-method
      2. 使用注解的方式 @PostConstruct
      3. 执行初始化后置方法 BeanPostProcessor
  4. 使用Bean
  5. 销毁Bean
    1. 销毁容器的各种方法,如 @PreDestroyDisposableBean 接口方法、destroy-method

比如我们现在需要买一栋房子,那么我们的流程是这样的:

  1. 先买房(实例化,从无到有)
  2. 装修(设置属性)
  3. 买家电,如洗衣机,冰箱,电视,空调等(各种初始化,可以入住)
  4. 入住(使用Bean)
  5. 卖房(Bean销毁)

执行流程如下图所示:

源码阅读

创建Bean的代码入口在 AbstractAutowireCapableBeanFactory#createBean

java 复制代码
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
    implements AutowireCapableBeanFactory {
    
    protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
            throws BeanCreationException {
        //...代码省略
        try {
            // 在实例化之前, 是否有快捷创建的Bean, 也就是通过PostProcessorsBeforeInstantiation返回的Bean
            // 如果存在, 则会替代原来正常通过target bean生成的bean的流程
            Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
            if (bean != null) {
                return bean;
            }
        }
        catch (Throwable ex) {
            throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
                    "BeanPostProcessor before instantiation of bean failed", ex);
        }
        
        try {
            // 创建Bean
            // 方法中包含了实例化、属性赋值、初始化过程
            Object beanInstance = doCreateBean(beanName, mbdToUse, args);
            if (logger.isTraceEnabled()) {
                logger.trace("Finished creating instance of bean '" + beanName + "'");
            }
            return beanInstance;
        }
        //...代码省略
    }
}

点进去继续看源码:AbstractAutowireCapableBeanFactory#doCreateBean

java 复制代码
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
        throws BeanCreationException {
    // 创建bean实例
    //...代码省略
    if (instanceWrapper == null) {
        // 实例化Bean
        instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
    //...代码省略
    
    //初始化bean实例
    Object exposedObject = bean;
    try {
        // 依据bean definition 完成bean属性赋值
        populateBean(beanName, mbd, instanceWrapper);
        
        // 执行bean初始化
        exposedObject = initializeBean(beanName, exposedObject, mbd);
    }
    catch (Throwable ex) {
        if (ex instanceof BeanCreationException &&
                beanName.equals(((BeanCreationException) ex).getBeanName())) {
            throw (BeanCreationException) ex;
        }
        else {
            throw new BeanCreationException(
                mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
        }
    }
    //...代码省略
    return exposedObject;
}

这三个方法与三个生命周期阶段一一对应

  1. createBeanInstance():实例化
  2. populateBean():属性赋值
  3. initializeBean():初始化

继续点进去 initializeBean()

java 复制代码
// 初始化Bean
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
    if (System.getSecurityManager() != null) {
        AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
            invokeAwareMethods(beanName, bean);
            return null;
        }, getAccessControlContext());
    }
    else {
        // 调用的三个Bean开头的Aware方法
        invokeAwareMethods(beanName, bean);
    }
    
    Object wrappedBean = bean;
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }
    
    try {
        // 调用初始化方法
        invokeInitMethods(beanName, wrappedBean, mbd);
    }
    catch (Throwable ex) {
        throw new BeanCreationException(
                (mbd != null ? mbd.getResourceDescription() : null),
                beanName, "Invocation of init method failed", ex);
    }
    
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }
    
    return wrappedBean;
}

// 调用的三个Bean开头的Aware方法
private void invokeAwareMethods(String beanName, Object bean) {
    if (bean instanceof Aware) {
        if (bean instanceof BeanNameAware) {
            ((BeanNameAware) bean).setBeanName(beanName);
        }
        if (bean instanceof BeanClassLoaderAware) {
            ClassLoader bcl = getBeanClassLoader();
            if (bcl != null) {
                ((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
            }
        }
        if (bean instanceof BeanFactoryAware) {
            ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
        }
    }
}

Ⅲ. SpringBoot自动配置

SpringBoot 的自动配置就是当 Spring 容器启动后,一些配置类、bean 对象等就自动存入到了 IoC 容器中,不需要我们手动去声明,从而简化了开发,省去了繁琐的配置操作。

一、Spring 加载 Bean

1. 问题描述

需求:使用 Spring 管理第三方的 jar 包的配置

引入第三方的包,其实就是在该项目下,引入第三方的代码,我们采用在该项目下创建不同的目录来模拟第三方的代码引入

第三方文件代码:

java 复制代码
@Component
public class MyConfig {
    public void func() {
        System.out.println("start func() ...");
    }
}

获取 MyConfig 这个 Bean:

java 复制代码
@SpringBootTest
class BeanTheoryApplicationTests {
    @Autowired
    private ApplicationContext applicationContext;

    @Test
    void contextLoads() {
        MyConfig myConfig = applicationContext.getBean(MyConfig.class);
        System.out.println(myConfig);
    }
}

运行后报错:

2. 原因分析

Spring 通过五大注解和 @Bean 注解可以帮助我们把 Bean 加载到 Spring IoC 容器中,以上有个前提就是这些注解类需要和 SpringBoot 启动类在同一个目录下@SpringBootApplication 标注的类就是 SpringBoot 项目的启动类)

当我们引入第三方的 Jar 包时,第三方的 Jar 代码目录肯定不在启动类的目录下,如何告诉 Spring 帮我们管理这些 Bean 呢?

3. 解决方案

常见的解决方案有两种:

  1. @ComponentScan 组件扫描
  2. @Import 导入(使用 @Import 导入的类会被 Spring 加载到 IoC 容器中)
@ComponentScan

在启动类中添加扫描路径:

java 复制代码
@ComponentScan("com.liren.third")
@SpringBootApplication
public class BeanTheoryApplication {
    public static void main(String[] args) {
        SpringApplication.run(BeanTheoryApplication.class, args);
    }
}

也可以指定扫描多个包:

java 复制代码
@ComponentScan({"com.liren.autoconfig", "com.example.demo"})

Spring 是否使用了这种方式呢?

非常明显,没有。(因为我们引入第三方框架时,没有加扫描路径。比如mybatis)

如果 SpringBoot 采用这种方式,当我们引入大量的第三方依赖,比如 Mybatisjackson 等时,就需要在启动类上配置不同依赖需要扫描的包,这种方式会非常繁琐。

@Import

@Import 导入主要有以下几种形式:

  1. 导入类
  2. 导入 ImportSelector 接口实现类
1. 导入类
java 复制代码
@Import(MyConfig.class)
@SpringBootApplication
public class BeanTheoryApplication {
    public static void main(String[] args) {
        SpringApplication.run(BeanTheoryApplication.class, args);
    }
}

也可以采用导入多个类:

java 复制代码
@Import({MyConfig1.class, MyConfig2.class})
@SpringBootApplication
public class BeanTheoryApplication {
    public static void main(String[] args) {
        SpringApplication.run(BeanTheoryApplication.class, args);
    }
}

很明显,这种方式也很繁琐,所以 SpringBoot 依然没有采用这种方式!

2. 导入 ImportSelector 接口实现类

首先写一个 ImportSelector 接口实现类,重写里面的 selectImports() 方法:

java 复制代码
public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"com.liren.third.MyConfig"};
    }
}

启动类中导入我们自定义的 MyImportSelector

java 复制代码
@Import(MyImportSelector.class)
@SpringBootApplication
public class SpringAutoconfigApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringAutoconfigApplication.class, args);
    }
}
如何解决上述两种方式的问题❓

这两种方式都有一个明显的问题,就是使用者需要知道第三方依赖中有哪些 Bean 对象或配置类。如果漏掉其中一些 Bean,很可能导致我们的项目出现大的事故。

依赖中有哪些 Bean,使用时需要配置哪些 Bean,第三方依赖最清楚,那能否由第三方依赖来做这件事呢?

  • 比较常见的方案就是第三方依赖给我们提供一个注解 ,这个注解一般都以 @EnableXxxx 开头的注解,注解中封装的就是 @Import 注解
  1. 第三方依赖提供注解:注解中封装 @Import 注解,导入 MyImportSelector.class

    java 复制代码
       @Retention(RetentionPolicy.RUNTIME)
       @Target(ElementType.TYPE)
       @Import(MyConfig.class)
       public @interface EnableMyConfig {
       }
  2. 在启动类上使用第三方提供的注解

java 复制代码
@EnableMyConfig
@SpringBootApplication
public class BeanTheoryApplication {
  public static void main(String[] args) {
      SpringApplication.run(BeanTheoryApplication.class, args);
  }
}

这种方式也可以导入第三方依赖提供的 Bean,并且这种方式更优雅一点,SpringBoot 采用的也是这种方式。

二、自动装配原理

1. 源码阅读

一切的来自起源 SpringBoot 的启动类开始:(@SpringBootApplication 标注的类就是 SpringBoot 项目的启动类)

java 复制代码
@SpringBootApplication
public class SpringIocApplication {
    public static void main(String[] args) {
        // 获取Spring上下文对象
        ApplicationContext context = SpringApplication.run(SpringIocApplication.class, args);
        
        // 从Spring上下文中获取对象
        BeanLifeComponent beanLifeComponent = context.getBean(BeanLifeComponent.class);
        beanLifeComponent.use();
    }
}

这个类和普通类唯一的区别就是 @SpringBootApplication 注解,这个注解也是 SpringBoot 实现自动配置的核心。

这个注解的内容如下所示:

  1. JDK中提供了4个标准的用来对注解类型进行注解的注解类,我们称之为meta-annotation(元注解),它们分别是:
  • @Target:描述注解的使用范围(即被修饰的注解可以用在什么地方)
  • @Retention:描述注解保留的时间范围
  • @Documented:描述在使用javadoc工具为类生成帮助文档时是否要保留其注解信息
  • @Inherited :使被它修饰的注解具有继承性(如果某个类使用了被 @Inherited 修饰的注解,则其子类将自动具有该注解)

2. @EnableAutoConfiguration 详解

java 复制代码
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({AutoConfigurationImportSelector.class})
@AutoConfigurationPackage
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
    Class<?>[] exclude() default {};
    String[] excludeName() default {};
}
@Import({AutoConfigurationImportSelector.class})
  1. 使用 @Import 注解,导入了实现 ImportSelector 接口的实现类:
java 复制代码
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        } else {
            // 获取自动配置的配置类信息
            AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
            return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
        }
    }
}

selectImports() 方法底层调用 getAutoConfigurationEntry() 方法,获取可自动配置的配置类信息集合:

java 复制代码
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    if (!this.isEnabled(annotationMetadata)) {
        return EMPTY_ENTRY;
    } else {
        AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
        
        // 获取在配置文件中配置的所有自动配置类的集合
        List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
        
        configurations = this.removeDuplicates(configurations);
        Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
        this.checkExcludedClasses(configurations, exclusions);
        configurations.removeAll(exclusions);
        configurations = this.getConfigurationClassFilter().filter(configurations);
        this.fireAutoConfigurationImportEvents(configurations, exclusions);
        return new AutoConfigurationEntry(configurations, exclusions);
    }
}

getAutoConfigurationEntry() 方法通过调用 getCandidateConfigurations(...) 方法获取在配置文件中配置的所有自动配置类的集合:

java 复制代码
// 获取所有基于
// META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件
// META-INF/spring.factories文件中配置类的集合
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    ImportCandidates importCandidates = ImportCandidates.load(this.autoConfigurationAnnotation,
       getBeanClassLoader());
    List<String> configurations = importCandidates.getCandidates();
    Assert.notEmpty(configurations,
           "No auto configuration classes found in " + "META-INF/spring/"
                 + this.autoConfigurationAnnotation.getName() + ".imports. If you "
                 + "are using a custom packaging, make sure that file is correct.");
    return configurations;
}

获取所有基于 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件(在 IDEA 中双击 shift 之后打开查看)、META-INF/spring.factories 文件中配置类的集合。

在引入的起步依赖中,通常都有包含以上两个文件:

  1. 在加载自动配置类的时候,并不是将所有的配置全部加载进来 ,而是通过 @Conditional 等注解的判断进行动态加载。
    1. @Conditionalspring 底层注解,意思就是根据不同的条件,来进行自己不同的条件判断,如果满足指定的条件,那么配置类里边的配置才会生效。
  2. META-INF/spring.factories 文件是 Spring 内部提供的一个约定俗成的加载方式,只需要在模块的 META-INF/spring.factories 文件中配置即可,Spring就会把相应的实现类注入到容器中。

比如 Redis 的配置:RedisAutoConfiguration

可以看到,配置文件中使用 @Bean 声明了一些对象,spring 就会自动调用配置类中使用 @Bean 标识的方法,并把对象注册到 Spring IoC 容器中。

@AutoConfigurationPackage
java 复制代码
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({AutoConfigurationPackages.Registrar.class})
public @interface AutoConfigurationPackage {
    String[] basePackages() default {};
    Class<?>[] basePackageClasses() default {};
}

这个注解主要是导入一个配置文件 AutoConfigurationPackages.Registrar.class

java 复制代码
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
       register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
    }

    @Override
    public Set<Object> determineImports(AnnotationMetadata metadata) {
       return Collections.singleton(new PackageImports(metadata));
    }
}

Registrar 实现了 ImportBeanDefinitionRegistrar 类,就可以被注解 @Import 导入到 spring 容器里。

其中 (new PackageImports(metadata)).getPackageNames().toArray(new String[0]) 表示当前启动类所在的包名。

结论: @AutoConfigurationPackage 就是将启动类所在的包下面所有的组件都扫描注册到 spring 容器中

3. 总结

SpringBoot 自动配置原理的大概流程如下:

SpringBoot 程序启动时,会加载配置文件当中所定义的配置类,通过 @Import 注解将这些配置类全部加载到 SpringIOC 容器中,交给 IOC 容器管理。

相关推荐
liuhaikang1 分钟前
鸿蒙高性能动画库——lottie-turbo
java·开发语言·nginx
面对疾风叭!哈撒给6 分钟前
Liunx之Docker 安装启动 influxdb2
java·spring cloud·docker
沛沛老爹7 分钟前
Web开发者快速上手AI Agent:基于Function Calling的提示词应用优化实战
java·人工智能·llm·agent·web·企业开发·function
羊小猪~~8 分钟前
数据库学习笔记(十八)--事务
数据库·笔记·后端·sql·学习·mysql
麦兜*11 分钟前
Spring Boot 启动过程全解析:从main方法到Tomcat启动的魔法之旅
java·spring boot·后端·spring·tomcat·firefox
GrowingYi15 分钟前
Go语言的特性
开发语言·后端·golang
零度@18 分钟前
Java-Redis 缓存「从入门到黑科技」2026 版
java·redis·缓存
冬奇Lab19 分钟前
Vercel部署全攻略:从GitHub到上线,10分钟让你的前端项目免费拥有自己的域名
前端·后端·node.js
zzhongcy19 分钟前
多级缓存对比(Caffeine + Redis),以及缓存不一致问题的解决
java
宁在春19 分钟前
【2025 年终总结】人好像真的只活那么几个瞬间
后端·程序员·年终总结