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

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只执行一次。

相关文章:

  • JSON是什么
  • 【Git “reset“ 命令详解】
  • 论文浅尝 | C-ICL:用于信息抽取的对比式上下文学习(EMNLP2024)
  • 淘宝获取商品sku详情API接口如何调用?
  • 转发和重定向
  • 每天认识一个设计模式-桥接模式:在抽象与实现的平行宇宙架起彩虹桥
  • GPT-4o图像生成功能:技术突破与隐忧并存
  • R 安装和查看历史版本 R 包指南
  • 在Vue 3 + TypeScript + Vite 项目中安装和使用 SCSS
  • 如何在 Postman 中上传图片并在请求中正确引用?
  • 蓝桥杯 班级活动
  • 【学习笔记】大模型架构设计与长上下文能力的实现
  • jmeter 工具安装及并发压测详解
  • PostgreSQL学习之一次一密口令认证(TOTP)
  • 【Git】git cherry-pick(将某个分支的 commit 改动复制到当前分支)
  • 诠视科技MR眼镜如何安装apk应用
  • 《Linux运维实战:Ubuntu 22.04使用pam_faillock实现登录失败处理策略》
  • Linux Shell 脚本使用YAD工具实现Shell图形化界面
  • CodeBrick笔记,一种支持低功耗的嵌入式操作系统
  • 【TCP/IP、HTTP等网络协议】
  • 体坛联播|欧冠巴萨3比3战平国米,柯洁未进入国家集训队
  • 神舟十九号航天员乘组平安抵京
  • 外媒称菲方允许菲官员窜台,国台办:应停止在台湾问题上玩火
  • 事关广大农民利益,农村集体经济组织法5月1日起施行
  • 铁路上海站今日预计发送旅客65.8万人次,同比增长超16%
  • 浙商银行一季度净赚超59亿微增0.61%,非息净收入降逾22%