当前位置: 首页 > article >正文

Springbean(二)@Component及其派生注解自动注入(2)使用注意和加载问题

一、加载顺序

1、默认扫描顺序

Spring 在 @ComponentScan 或者 @SpringBootApplication 默认的包扫描路径下,会按照类路径扫描所有的 @Component 及其派生注解(如 @Service, @Repository, @Controller)的 Bean,并将其注册到 Spring 容器中。

(1)默认情况下,扫描的顺序是不确定的,因为 Spring 使用反射扫描类路径,而类路径的顺序通常依赖于底层文件系统或者 ClassLoader 的实现。即使这个bean继承了ApplicationContextAware、InitializingBean、或者bean的某个接口使用了@PostConstruct注解,这些Bean的加载顺序是不确定的,见:

SpringBean模块(二)bean初始化(2)和容器初始化顺序的比较--引入ApplicationContextInitializer-CSDN博客

(2)但如果 @ComponentScan 通过 basePackages 指定了多个包路径,Spring 会按声明的顺序扫描这些包。其实也只是包有了顺序,实际上类还是按照类的路径加载,也就是无序的。

2、依赖关系对扫描顺序的影响

上面提到在没有依赖关系的情况下,bean的加载顺序是随机的。

而如果一个 @Component 需要依赖另一个 Bean,Spring 会确保 先初始化被依赖的 Bean,再初始化依赖它的 Bean。

(1)使用@Autowired等自动装配

见SpringBean模块(二)bean初始化(2)和容器初始化顺序的比较--引入ApplicationContextInitializer-CSDN博客

(2)构造函数的依赖
@Component
public class A {
    public A(B b) {
        System.out.println("A initialized");
    }
}

@Component
public class B {
    public B() {
        System.out.println("B initialized");
    }
}


输出:
B initialized
A initialized
3、显示顺序调整问题
(1)@DependsOn显示指明顺序
@Component
@DependsOn("c")
public class D {
    public D() {
        System.out.println("D initialized");
    }
}

@Component
public class C {
    public C() {
        System.out.println("C initialized");
    }
}

输出:
C initialized
D initialized
 (2)@Order影响 ListSet 类型的 Bean

如果多个 @Component 实现了同一个接口,并且注入为 ListSet 类型,Spring 会根据 @Order 注解的值来决定顺序:

@Component
@Order(2)
public class First implements MyInterface { }

@Component
@Order(1)
public class Second implements MyInterface { }

当它们被注入 List<MyInterface> 时,顺序会是 SecondFirst

但**@Order 只影响 List/Set 注入的顺序,不影响 Bean 的初始化顺序**。

 (3)@Primary@Qualifier 影响注入选择

如果多个 @Component 适用于相同类型的注入:

  • @Primary 标注的 Bean 会优先被注入。

  • @Qualifier 可以精确指定要注入哪个 Bean,但不影响初始化顺序。

(4)懒加载 @Lazy 可能会延迟初始化

如果 @Component 标注了 @Lazy,它的 Bean 只有在被使用时才会初始化,而不是在 Spring 容器启动时初始化:如果该 Bean 没有被使用,它就不会初始化。

@Component
@Lazy
public class LazyBean {
    public LazyBean() {
        System.out.println("LazyBean initialized");
    }
}

二、加载原理对比

1、@configuration和@component

使用@component+@Bean与@configuration+@Bean,加载的区别很大

(1)@component+@Bean

不会被Spring代理,会直接获取一个全新的Bean对象实例所以全局会有多个Bean对象的实例;而

(2)@configuration注解的@Bean

   全局只有一个Bean对象。

Spring 容器在启动时,会加载默认的一些PostProcessor,其中就有ConfigurationClassPostProcessor,这个后置处理程序专门处理带有@Configuration注解的类,这个程序会在bean 定义加载完成后,在bean初始化前进行处理。主要处理的过程就是使用cglib动态代理增强类,而且是对其中带有@Bean注解的方法进行处理。

Spring发现方法所请求的Bean已经在容器中,那么就直接返回容器中的Bean。所以全局只有一个SimpleBean对象的实例。

(3)demo
package com.bit.demo.test.bean;

import com.bit.demo.dto.UserDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

@Component
@Slf4j
public class CompBean {
    @Bean
    public UserDTO initUser() {
        log.info("CompBean获取user");
        UserDTO user = new UserDTO();
        user.setUserName("zs");
        return user;
    }
}
package com.bit.demo.test.bean;

import com.bit.demo.dto.RoleDTO;
import com.bit.demo.dto.UserDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

@Configuration
@Slf4j
public class ConfigBean {
    @Bean
    public RoleDTO initRole() {
        log.info("ConfigBean 获取user");
        RoleDTO user = new RoleDTO();
        user.setRoleName("school");
        return user;
    }
}
单测
    @Autowired
    private CompBean compBean;

    @Autowired
    private ConfigBean configBean;

    @Test
    public void testBean(){
        compBean.initUser();
        configBean.initRole();
    }

执行输出

2025-03-28T16:43:08.200+08:00  INFO 21600 --- [           main] com.bit.demo.test.bean.CompBean          : CompBean获取user
2025-03-28T16:43:08.201+08:00  INFO 21600 --- [           main] com.bit.demo.test.bean.ConfigBean        : ConfigBean 获取user
2025-03-28T16:43:08.587+08:00  INFO 21600 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port 52533 (http) with context path '/bitDemo'
2025-03-28T16:43:08.594+08:00  INFO 21600 --- [           main] com.bit.demo.UserSysFlagTest             : Started UserSysFlagTest in 2.366 seconds (process running for 2.916)
Java HotSpot(TM) 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended
2025-03-28T16:43:09.447+08:00  INFO 21600 --- [           main] com.bit.demo.test.bean.CompBean          : CompBean获取user

可见,@Configuration+@Bean只执行一次。


http://www.kler.cn/a/612571.html

相关文章:

  • 一周掌握Flutter开发--8. 调试与性能优化(下)
  • 计算机网络--传输层(1)
  • html dom 的 event 事件
  • 【Elasticsearch基础】基本核心概念介绍
  • [实操]MySQL8 读写分离后,配合redis的方法与步骤
  • pnpm 依赖升级终极指南:从语义化版本控制到 Monorepo 全局更新的企业级实践
  • ComfyUi教程之阿里的万象2.1视频模型
  • Redis学习二
  • 级联FFT(超采样FFT架构)的MATLAB代码及原理
  • ip改变导致的数据库连接不上
  • Selenium之八大定位
  • browser-use 库网页自动化截图
  • SRT协议
  • 【AI News | 20250323】每日AI进展
  • 为 AI 应用打造安全屏障:基于 Dify 的完整实践
  • 分布式锁,redisson,redis
  • 渗透测试过-关于学习Token、JWT、Cookie等验证授权方式的总结
  • 数据结构栈和队列
  • 前端面经分享(25/03/26)
  • ICRA-2025 | 从人类视角到机器人视角的具身导航!连续环境中基于地面视角的视觉语言导航