【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 容器管理。

相关推荐
葫芦和十三1 天前
图解 MongoDB 21|选举与 failover:Primary 是怎么选出来的
后端·mongodb·agent
GetcharZp1 天前
26k Star 开源内网穿透神器 NetBird,一分钟实现全球设备互联!
后端
考虑考虑1 天前
Mybatis实现批量插入
java·后端·mybatis
咖啡八杯1 天前
GoF设计模式——中介者模式
java·后端·spring·设计模式
lizhongxuan1 天前
多Agent之间的区别
后端
青石路1 天前
记一次多JDK版本问题的排查,一坑套一坑,差点没爬上来
java
杨充1 天前
1.面向对象设计思想
后端
IT_陈寒1 天前
Java的Date类又坑了我一次,改用时间戳真香
前端·人工智能·后端
systemPro1 天前
2.6亿条设备数据,历史查询从超时到50ms,我做了什么
后端
要阿尔卑斯吗1 天前
提示词优化启示:为什么“按顺序输出“比“关键度评分“更有效
后端