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

SpringBoot--Bean管理详解

Bean管理

Bean扫描

回顾spring:

在XML配置文件中,可以借助

 <context:component-scan base-package = "com.lyc">

或者注解

 @ComponentScan(basePackages="com.lyc")

再springboot项目中,既没有标签,也没有写注解,但他仍然可以扫描到我们写的业务代码。这是为什么呢?

原因如下:

在springboot启动类中的@springBootApplication是一个组合注解。

 @SpringBootConfiguration@EnableAutoConfiguration@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })public @interface SpringBootApplication {

其中就组合了@ComponentScan注解,因此可以扫描Bean对象。

注意事项:这相当于在启动类上添加了@ComponentScan注解,所以默认只能扫描启动类所在的包及其子包,而其他包扫描不到。如果想扫描其他包,需要手动添加@ComponentScan注解。

Bean注册

回顾spring:

在spring项目中,我们可以添加以下注解来进行Bean的注册

注解说明位置
@Component声明bean的基础注解不属于以下三类时,用此注解
@Service@Component的衍生注解标注在业务类上
@Controller@Component的衍生注解标注在控制类上
@Repository@Component的衍生注解标注在数据访问类上(由于与MyBatis整合,用得少)

问题:在spring项目中,如果将三方jar包中的Bean对象注入到IOC容器中,这些注解还能使用吗?

解答:不能,在spring项目中这些jar包被设为只读,不能修改,因此:

如果要注册的Bean对象来自第三方(不是自定义),是无法使用@Component及衍生注解的声明Bean的

如何解决?

spring为我们提供了两个注解解决问题:

  • @Bean

  • @Import

前置知识:

如何将第三方jar包通过Maven坐标引入?

先使用maven命令将第三方jar包安装在本地仓库

mvn install:install-file 	-Dfile=jar包所在本地磁盘的路径-DgroupId=组织名称-DartifactId=项目名称-Dversion=版本号-Dpackaging=打包方式

再在pom依赖中导入坐标即可。

<dependency><groupId>组织名称</groupId><artifactId>项目名称</artifactId><version>版本号</version>
</dependency>
@Bean的使用

(@Bean只写在方法上,返回的是一个对象,但一般不获取已经在容器中的对象)

使用@Bean注解将第三方的Bean对象注入到IOC容器中,可以在启动类里面去声明一个方法,这个方法里去创建一个对象

再在这个方法上声明一个@Bean注解(将方法返回值交给IOC容器管理,成为IOC容器的Bean对象)。

public class Myconfig {@Bean("User")//相当于XML中的<bean id="user" class="com.lyc.pojo.User"/>//这个方法的名字,就相当于bean标签中的id值//这个方法的返回值,就相当于bean标签中的class属性的值public User getUser(){return new User();//返回要注入到bean的对象}
}

如何证明注入IOC容器成功呢? 将对应类型的对象拿出来就好了。

在springboot项目中的启动方法(run()方法),run()方法会将spring初始化好的容器返回。通过容器来调用getBean方法拿到对应类想的Bean对象。

ConfigurableApplicationContext context = SpringApplication.run(SpringbootMybatisApplication.class, args);
User user = context.getBean(User.class);
System.out.plintln(user)

但是这种方式并不推荐使用。因为在Java代码中类的设计遵循一个单一职责原则,一个类只负责一个功能,启动类就应该只负责启动,不应该再加一个注册的功能。

如果说要配置一个第三方的Bean,建议在配置类中集中注册。就比如:在新增一个config包,里面专门来写配置类。

@Configuration//这个也被spring容器托管,注册到容器中,因为它本身也是一个@Component,表明这是一个配置类,相当于XML当中的 <beans>
@ComponentScan("com.lyc.pojo")//@ComponmentScan是扫描主程序同级的包,所以分层包放在其他地方,就会发生错误
@Import(MyConfig2.class)
public class Myconfig {@Bean("User")//相当于XML中的<bean id="user" class="com.lyc.pojo.User"/>//这个方法的名字,就相当于bean标签中的id值//这个方法的返回值,就相当于bean标签中的class属性的值public User getUser(){return new User();//返回要注入到bean的对象}
}

@Configuration

  • 作用@Configuration 注解用于定义一个配置类,表示该类中有一个或多个 @Bean 方法。@Configuration 是一个特化版的 @Component,它会告诉 Spring 该类是一个配置类,能够包含 Bean 定义方法,并且支持代理机制。

  • 使用场景:用于配置类的定义,通常与 @Bean 一起使用,用来显式声明 Bean。在 Spring Boot 项目中,它常用于设置应用的配置,或者在传统的 Spring 项目中配置一些自定义 Bean。

@Configuration
public class AppConfig {@Beanpublic MyService myService() {return new MyServiceImpl();}
}

@Component 不同,@Configuration 表明这是一个配置类,其中可以定义多个 @Bean 方法,而这些 Bean 会以一种更为严格和优化的方式加载到 Spring 容器中(通过代理机制)。

注意事项:

  • @Bean只能声明方法。

  • @Bean("名称")//相当于XML中的< bean id="名称" class="对象实体类路径"/ >

  • 这个方法的名字,就相当于bean标签中的id值

  • 这个方法的返回对象,就相当于bean标签中的class属性的值。

  • 如果方法的内部需要使用IOC容器中的Bean对象,只需要在方法上声明即可,spring会自动注入

@Import的使用

常用方式

  • 导入配置类(手动扫描)

  • 导入ImportSelector 接口实现类

  • @EnableXxxx注解,用于封装@Import注解

举例说明:

当我们的配置类不在启动类的包名下时,启动类是无法扫描到我们的配置类的,我们可以在启动类上添加@Import(配置类的字节码文件),这样就spring就会把配置类文件中对应的Bean对象注入到IOC容器中。

但我们需要导入的配置类过多时,我们可以使用数组的形式,来注入配置类文件,@Import为我们提供了方法。

package org.springframework.context.annotation;​import java.lang.annotation.Documented;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;​@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface Import {Class<?>[] value();} //以数组的方式进行注入

但当我们的配置类文件过多时,以数组形式注入,也会显得代码冗余,如此便用到了我们的第二个方式:导入ImportSelector 接口实现类

  • 首先定义一个类去实现ImportSelector接口。

  • 重写selectImports方法,在方法中只需要返回一个字符串的数组。而数组中里面的每一个字符串就是要注入到IOC容器中的Bean对象的全类名。

注意事项:springboot会自动调用selectImports方法得到里面数组的内容,然后将这些类的Bean对象自动注入到IOC容器中

代码展示:

public class CommonImportSelector implements ImportSelector{
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata){List<String> imports = new ArrayList<>();return new String[]{"com.lyc.config.CommonConfig"};
}
}

这时只需要在启动类上声明@Import(CommonImportSelector.class)即可。

注意事项:在实际开发中,数组的内容一般不会写死,,基本上都是从配置文件中读取出来的。这样就降低了代码的耦合性,是代码之间更加灵活,程序员只需要将全类名,写入到配置文件中即可。

代码展示:

common.imports

# 一行写一个全类名即可
com.lyc.config.CommonConfig

CommonImportSelector.java

 public class CommonImportSelector implements ImportSelector{@Overridepublic String[] selectImports(AnnotationMetadata importingClassMetadata){//读取配置文件内容List<String> imports = new ArrayList<>(); //初始化数组Inputstream is =  CommonImportSelector.class.getClassLoader().getResourceAsStream("common.imports");//通过反射拿到class对象在调用方法将配置文件转换为字节流,字节流不适合文本文件的读取,需要将其转换为字符流BufferedReader br = new BufferedReader(new InputStreamReader(is));//将字节流通过转换流装换成字符流,在使用字符缓冲流提高读写性能,String line = null; // 定义String类型的变量try{while((line = br.readLine())!=null){ //逐行读取数据,只要数据不为空,则放在imports数组中imports.add(line);}}catch(Exception e){throw new RuntimeException(e);}finally{if(br != null){try{br.close();//关闭流}catch (Exception e){throw new RuntimeException(e);}}}return imports.toArray(new String[0]);//new String[0] 只起到一个提供类型的作用}}

如何在进一步,让代码显得更简洁呢?

在启动类中定义一个组合注解,将@Import注解组合一下,在启动类中添加组合注解即可

 package com.lyc.springbootmybatis.annotation;​import org.springframework.context.annotation.Import;​import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;​@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Import(CommonImportSelector.class)public @interface EnableCommonConfig {}

然后再在启动类上声明@EnableCommonConfig即可,这样的优点是提高了代码的安全性,缺点就是别人不好维护。

在springboot中的源码就有很多这样的EnableXxxx注解,说明在实际开发中这种方法被大众所接受

注册条件

注入到IOC容器中的Bean对象如何插入数据?

  • 可以在新建对象时为对象赋值,之后再进行注入。

这种方法是可以插入数据,但是又是和Java代码高度耦合,修改代码是极其繁琐,因此不建议这样做。

  • 也可以将对象属性值写入配置文件,再通过@Value导入配置文件,实现与Java代码的解耦。

代码展示:

application.yml

 User: name: nihaoage: 19

 public class Myconfig {@Bean("User")public User getUser(@Value("${User.name}") String name,@Value("${User.age}") int age){User user = new User();user.setName(name);user.setAge(age);return new User();//返回要注入到bean的对象}}

当我们将配置文件注释之后,再次运行,结果发现,程序报错,和我们的设想不一致,我们设想,有配置文件则选择注入,没有配置文件,就不去注入即可,那么如何去实现?

解析:

springboot提供了设置注册生效条件的注解@Conditional

可以借助这个注解来设置并注册条件,该注解使用较为繁琐,因此springboot官方提供了不少衍生注解来使用,

常用衍生注解:

注解说明
@ConditionalOnProperty()配置而文件中存在对应的属性时,该注解声明下的Bean才会被注册
@ConditionalOnMissingBean()当不存在当前类型的Bean时,该注解声明下的Bean才会被注册
@ConditionalOnClass()当前环境存在指定的类时,该注解声明下的Bean才会被注册

这个注解如何解决我们刚才的问题呢?

我们的要求:如果配置文件中配置了指定的信息,则注入,否则不注入。

代码展示:

 public class Myconfig {@ConditionalOnProperty(prefix = "User",name = {"name,age"})@Bean("User")public User getUser(@Value("${User.name}") String name,@Value("${User.age}") int age){User user = new User();user.setName(name);user.setAge(age);return new User();//返回要注入到bean的对象}}

测试结果:当将配置文件注释之后,User对象并没有被注入到IOC容器中

当配置文件没有被注释时,User Bean对象被注入到IOC容器中

需求:如果IOC容器不存在User,则注入Employee对象,否则不注入

 public class Myconfig {@ConditionalOnProperty(prefix = "User",name = {"name,age"})@Bean("User")public User getUser(@Value("${User.name}") String name,@Value("${User.age}") int age){User user = new User();user.setName(name);user.setAge(age);return new User();//返回要注入到bean的对象}@Bean("employee")@ConditionalOnMissingBean(User.class)public Employee getEmployee(){return new Employee();}}

测试结果:当将配置文件注释之后, @ConditionalOnProperty(prefix = "User",name = {"name,age"})注解检测不到属性,则不将User对象注入到IOC容器中,又 @ConditionalOnMissingBean(User.class) 注解检测到User对象在IOC容器中不存在,则将employee对象注入IOC容器。

当配置文件没有被注释时,@ConditionalOnProperty(prefix = "User",name = {"name,age"})注解检测到属性,则将User对象注入到IOC容器中,又 @ConditionalOnMissingBean(User.class) 注解检测到User对象存在与IOC容器,则不将employee对象注入IOC容器。

案例需求:

如果当前环境中存在DispatcherServlet类,则注入Employee,否则不注入。(如果项目中引入了web依赖,则环境中存在DispatcherServlet,否则没有)

image-20250514204153729

 @ConditionalOnClass(name = "org.springframework.web.servlet.DispatcherServlet")public Employee getEmployee(){return new Employee();}

当存在DispatcherServlet 类时,则将Employee对象注入到IOC容器中。如果不存在,则不注入。

以上就是Bean管理的详细内容,希望对大家有所帮助!

http://www.xdnf.cn/news/481627.html

相关文章:

  • 3D生成新突破:阶跃星辰Step1X-3D开源,可控性大幅提升
  • python + flask 做一个图床
  • ThinkPad X250电池换电池芯(理论技术储备)
  • windows系统中下载好node无法使用npm
  • STM32控制电机
  • duxapp 2025-03-29 更新 编译结束的复制逻辑等
  • MySQL的触发器
  • ubuntu 20.04 更改国内镜像源-阿里源 确保可用
  • 适合学校使用的桌面信息看板,具有倒计时、桌面时钟、课程表、天气预报、自动新闻联播、定时关机、消息通知栏、随机点名等功能。
  • CMU-15445(4)——PROJECT#1-BufferPoolManager-Task#2
  • 【软件工程】符号执行与约束求解缺陷检测方法
  • MySQL性能优化
  • RAG-MCP:基于检索增强生成的大模型工具选择优化框架
  • Cross-Site Scripting(XSS)
  • AT9850B—单北斗导航定位芯片
  • 【leetcode】94. 二叉树的中序遍历
  • 逻辑回归(二分类)
  • Linux的日志管理
  • 青少年ctf平台应急响应-应急响应2
  • 在vue3中使用Cesium的保姆教程
  • 【2025年软考中级】第一章1.5 输入输出技术(外设)
  • 代码随想录第51天|岛屿数量(深搜)、岛屿数量(广搜)、岛屿的最大面积
  • 2025年Ai写PPT工具推荐,这5款Ai工具可以一键生成专业PPT
  • Java—— 方法引用 : :
  • 【第76例】IPD流程实战:华为业务流程架构BPA进化的4个阶段
  • ROS2学习(3)------架构概述
  • 【数据仓库面试题合集①】数据建模高频面试题及解析
  • 平衡智慧在日常生活中的落地实践:构建和谐生活的行动指南
  • MYSQL创建索引的原则
  • 单例模式深度解析:从原理到高阶应用实践