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

Spring----ioc

1.Spring 是什么?

通过前⾯的学习, 我们知道了Spring是⼀个开源框架, 他让我们的开发更加简单. 他⽀持⼴泛的应⽤场景, 有着活跃⽽庞⼤的社区, 这也是Spring能够⻓久不衰的原因.

但是这个概念相对来说, 还是⽐较抽象.

我们⽤⼀句更具体的话来概括Spring, 那就是: Spring 是包含了众多⼯具⽅法的 IoC 容器

那问题来了,什么是容器?什么是 IoC 容器?接下来我们⼀起来看

2.Spring IoC 容器

2.1什么是IoC容器?

其实IoC我们在前⾯已经使⽤了,  我们在进行前后端访问时,在后端类上⾯添加 @RestController 和@Controller 注解, 就是把这个对象交给Spring管理, Spring 框架启动时就会加载该类. 把对象交给Spring管理, 就是IoC思想.

IoC: Inversion of Control (控制反转), 也就是说 Spring 是⼀个"控制反转"的容器.

什么是控制反转呢? 也就是控制权反转. 什么的控制权发⽣了反转? 获得依赖对象的过程被反转了也就是说, 当需要某个对象时, 传统开发模式中需要⾃⼰通过 new 创建对象, 现在不需要再进⾏创建, 把创建对象的任务交给容器, 程序中只需要依赖注⼊ (Dependency Injection,DI)就可以了.

这个容器称为:IoC容器. Spring是⼀个IoC容器, 所以有时Spring 也称为Spring 容器

 2.2Bean的存储

Bean是什么呢?Bean就是装入容器的对象。

要把Bean装入Spring中,需要借助@Component注解。

共有两类注解类型可以实现:

1. 类注解:@Controller、@Service、@Repository、@Component、@Configuration.

2. ⽅法注解:@Bean.

2.3Bean的获取

使⽤ @Controller 存储 bean 的代码如下所⽰:

@Controller
public class UserController {
    public void sayHi() {
        System.out.println("Hi");
    }

获取Bean需要通过ApplicationContext 也就是创建项目时程序的入口

ApplicationContext 翻译过来就是: Spring 上下⽂

因为对象都交给 Spring 管理了,所以获取对象要从 Spring 中获取,那么就得先得到 Spring 的上下⽂

这个上下⽂, 就是指当前的运⾏环境, 也可以看作是⼀个容器, 容器⾥存了很多内容, 这些内容是当前运⾏的环境

 通过以下代码获取Bean:

	public static void main(String[] args) {
		ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);
		//从Spring容器中获取对象
		UserController userController = context.getBean(UserController.class);
		System.out.println("userController:"+userController);
		userController.sayHi();
}

打印结果:

通过查看getBean源码

public interface BeanFactory {
 
 //以上省略...
 
 // 1. 根据bean名称获取bean
 Object getBean(String var1) throws BeansException;
 // 2. 根据bean名称和类型获取bean
 <T> T getBean(String var1, Class<T> var2) throws BeansException;
 // 3. 按bean名称和构造函数参数动态创建bean,只适⽤于具有原型(prototype)作⽤域的bean
 Object getBean(String var1, Object... var2) throws BeansException;
 // 4. 根据类型获取bean
 <T> T getBean(Class<T> var1) throws BeansException;
 // 5. 按bean类型和构造函数参数动态创建bean, 只适⽤于具有原型(prototype)作⽤域的
bean
 <T> T getBean(Class<T> var1, Object... var2) throws BeansException;
 
 //以下省略...
}

常⽤的是上述1,2,4种, 这三种⽅式,获取到的bean是⼀样的

其中1,2种都涉及到根据名称来获取对象. bean的名称是什么呢?

Bean 命名约定

我们看下官⽅⽂档的说明: Bean Overview :: Spring Framework

获取Bean的三种方法:

	public static void main(String[] args) {
		ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);
		//从Spring容器中获取对象
		UserController userController = context.getBean(UserController.class);
		System.out.println("userController:"+userController);
		userController.sayHi();

		//使用beanName获取对象
		UserController userController1 = (UserController)context.getBean("userController");
		System.out.println("userController1:"+userController1);
		userController1.sayHi();
		//使用beanName和类名获取对象
		UserController userController2 = (UserController)context.getBean("userController", UserController.class);
		System.out.println("userController2:"+userController2);
		userController2.sayHi();
	}

运行结果,显示表示的同一个对象 

注意如果没有将该对象放入容器(添加@Controller标签)就会报以下错误:

其他四个标签也是如此

		UserService userService = context.getBean(UserService.class);
		userService.doService();
		UserConfig userConfig = context.getBean(UserConfig.class);
		userConfig.doConfig();
		UserComponent userComponent = context.getBean(UserComponent.class);
		userComponent.doComponent();
		UserRepository userRepository = context.getBean(UserRepository.class);
		userRepository.doRepository();

2.4为什么要这么多类注解?

先看源码,这四个注解都有@Component

那么我们猜测这四个注解是由@Component延伸过来的,事实也正是如此。

这四个注解:

@Controller:表现层, 接收请求, 对请求进⾏处理, 并进⾏响应.

@Servie:业务逻辑层, 处理具体的业务逻辑.

@Repository:数据访问层,也称为持久层. 负责数据访问操作(Dao层)

@Configuration:配置层. 处理项⽬中的⼀些配置信息.

@Controller , @Service @Repository ⽤于更具体的⽤例(分别在控制层, 业务逻辑层, 持

久化层), 在开发过程中, 如果你要在业务逻辑层使⽤ @Component 或@Service,显然@Service是更好的选择

⽐如杯⼦有喝⽔杯, 刷⽛杯等, 但是我们更倾向于在⽇常喝⽔时使⽤⽔杯, 洗漱时使⽤刷⽛杯

2.5 ⽅法注解 @Bean

类注解是添加到某个类上的, 但是存在两个问题:

1. 使⽤外部包⾥的类, 没办法添加类注解

2. ⼀个类, 需要多个对象, ⽐如多个数据源

这种场景, 我们就需要使⽤⽅法注解 @Bean

例如:我们要获取jar包中的DataSourceLinkFactory对象(没有办法添加类注解)

这时我们采用@Bean注解

我们先尝试只用@Bean注解:

报错显示容器中没有此对象

2.5.1 ⽅法注解要配合类注解使⽤

在 Spring 框架的设计中,⽅法注解 @Bean 要配合类注解才能将对象正常的存储到 Spring 容器中,如下代码所⽰:

再次执行: 

2.5.2定义多个对象

对于同⼀个类, 如何定义多个对象呢?

⽐如多数据源的场景, 类是同⼀个, 但是配置不同, 指向不同的数据源

 定义一个Bean先不装入Spring

@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserInfo {
    private Integer id;
    private String name;
    private Integer age;

}

通过该方法装入 

@Configuration
public class UserInfoConfig {
    //在这个类中将UserInfo装入容器
    @Bean
    public UserInfo user1() {
        UserInfo userInfo = new UserInfo(1,"zhangsan", 12);
        return userInfo;
    }
    @Bean
    public UserInfo user2() {
        UserInfo userInfo = new UserInfo(2,"lisi", 13);
        return userInfo;
    }
}

然后获取UserInfo对象:

显示报错:对象不是唯一的,有两个对象,user1  user2

从这个报错信息中得知:

通过@Bean注解的bean,beanName是其方法名

正确的获取方法:使用beanName获取bean,获取了多个bean对象

BeanName定义总结:

1.五大注解:

       1)如果前两位均是大写,BeanName为类名

       2)其他的,BeanName为小驼峰写法

2.Bean注解(需要搭配五大注解使用,通常使用@Component或@Configuration)

    BeanName为方法名

同样,我们也可以通过为五大注解指定BeanName然后获取bean

通过默认 BeanName 获取bean

也可以指定BeanName后获取

但如果名字不正确,会报错:

Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'userRepository' available

2.5.3重命名 Bean

可以通过设置 name 属性给 Bean 对象进⾏重命名操作,如下代码所⽰:

    @Bean(name = "zhangsan")
    public UserInfo user1() {
        UserInfo userInfo = new UserInfo(1,"zhangsan", 12);
        return userInfo;
    }

如果仍然通过原名字获取就会报错

Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'user1' available

 用修改后的名字获取就成功:

在内部name属性是一个String数组

所以也可以同时定义多个名字,我们不管使用哪个名字都可以获取bean

    @Bean(name = {"zhangsan", "lisi"})
    public UserInfo user1() {
        UserInfo userInfo = new UserInfo(1,"zhangsan", 12);
        return userInfo;
    }

 

但是类注解内部value(没有指定就默认是value)不是数组,所以不能指定多个名字

3.扫描路径

Q: 使⽤前⾯学习的四个注解声明的bean,⼀定会⽣效吗?

A: 不⼀定(原因:bean想要⽣效,还需要被Spring扫描)

下⾯我们通过修改项⽬⼯程的⽬录结构,来测试bean对象是否⽣效:

SpringIocDemoApplication移动到其他包中

 

再运行(之前是正常运行的,只改变了结构)会报错

解释: 没有bean的名称为u1

为什么没有找到bean对象呢?

使⽤五⼤注解声明的bean,要想⽣效, 还需要配置扫描路径, 让Spring扫描到这些注解

项目在启动时,不会扫描整个项目(还导入了许多jar包呢,扫描要花很多时间),会从SpringIocDemoApplication的路径开始扫描

刚才就是因为扫描路径改为这个导致扫描不到其他注解。

我们可以通过 @ComponentScan 来配置扫描路径

@ComponentScan("com.bite.spring_ioc_demo")

放在SpringIocDemoApplication前面

运行成功

那为什么前⾯没有配置 @ComponentScan注解也可以呢?

@ComponentScan 注解虽然没有显式配置,但是实际上已经包含在了启动类声明注解 @SpringBootApplication 中了

默认扫描的范围是SpringBoot启动类所在包及其⼦包

这点通过SpringBootApplication 源码可以看出:

相关文章:

  • 解读GM2D二维码在线平台:全球二维码迁移计划的创新
  • WEB安全--SQL注入--利用log写入webshell
  • 您的数据是如何出现在暗网上的?
  • 深度学习论文: Image Segmentation Using Text and Image Prompts
  • 2025年IT行业技术革命全景解析:从AI到量子计算的落地实践
  • C - 创建初级通讯录(详细解析一)
  • MySQL--主从复制--原理
  • (UI自动化测试web端)第三篇:元素的常用操作方法_浏览器操作
  • QT编程之CAN协议
  • DeepSeek与GPT的全方位对比及其为编程工作带来的巨大变革
  • 熊猫精灵脚本助手官网_找图找色-脚本精灵2025最新版下载
  • 深入解析 Java 类加载机制及双亲委派模型
  • 【MARK-2小车】小车教程、上位机教程
  • 高等数学-第七版-上册 选做记录 习题5-4
  • 表的约束及代码练习
  • django入门教程之templates和static资源【五】
  • 八纲辨证总则
  • 如何优化 docker 镜像体积?
  • 【深度学习基础 2】 PyTorch 框架
  • EMQX Dashboard
  • 苏丹港持续遭无人机袭击,外交部:呼吁各方保护民用设施和平民安全
  • 事关中国,“英伟达正游说美国政府”
  • 国际观察丨美中东政策生变,以色列面临艰难选择
  • 中国田径巡回赛西安站完赛:男子跳远石雨豪夺冠
  • 墨西哥海军一载两百余人帆船撞上纽约布鲁克林大桥,多人落水
  • 2人恶意传播刘国梁谣言被处罚,媒体:以法律利剑劈谣斩邪,加快推进依法治体