JavaEE--SpringIoC - 详解

news/2025/11/13 18:22:29/文章来源:https://www.cnblogs.com/ljbguanli/p/19219144

JavaEE--SpringIoC - 详解

目录

一、IoC概述

1. 传统程序开发

2. 问题分析

3. 解决方案

4. IoC程序开发

5. IoC优势

二、IoC详解

1. Bean的存储

1.1 @Controller(控制器存储)

1.2 @Service(服务存储)

1.3 @Repository(仓库存储)

1.4 @Component(组件存储)

1.5 @Configuration(配置存储)

2. 为什么需要这么多注解?

3. 方法注解@Bean

3.1 方法注解要配合类注解使用

3.2 定义多个对象

3.3 重命名Bean

3.4 扫描路径


一、IoC概述

控制反转(Inversion of Control,IoC)是一种设计原则,用于解耦组件之间的依赖关系。传统编程中,调用者主动创建和管理依赖对象;而IoC将控制权交给外部容器或框架,由容器负责创建和注入依赖对象。

接下来我们通过案例来了解一下什么是IoC。

需求:造一辆车

1. 传统程序开发

我们的设计思路是这样的:

先设计轮子(Tire),然后根据轮子的大小设计底盘(Bottom),接着根据底盘设计车身(Framework),最后根据车身设计好整个汽车(Car)。这里就出现了一个“依赖”关系:汽车依赖车身,车身依赖底盘,底盘依赖轮子。

最终程序的实现代码如下:

public class NewCarExample {public static void main(String[] args) {Car car = new Car();car.run();}/*** 汽车对象*/static class Car {private Framework framework;public Car() {framework = new Framework();System.out.println("Car init...");}public void run() {System.out.println("Car run...");}}/*** 车身类*/static class Framework {private Bottom bottom;public Framework() {bottom = new Bottom();System.out.println("Framework init...");}}/*** 底盘类*/static class Bottom {private Tire tire;public Bottom() {this.tire = new Tire();System.out.println("Bottom init...");}}/*** 轮胎类*/static class Tire {// 尺寸private int size;public Tire() {this.size = 17;System.out.println("轮胎尺寸: " + size);}}
}

2. 问题分析

这样的设计看起来没问题,但是可维护性却很低。

如果接下来需求有了变更:随着对车的需求量越来越大,个性化需求也会越来越多,我们需要加工多种尺寸的轮胎。

那这个时候就需要对上面的程序进行修改,修改后的代码如下所示:

修改之后,其他调用程序也会报错,我们需要继续修改:

完整代码如下:

public class NewCarExample {public static void main(String[] args) {Car car = new Car(20);car.run();}/*** 汽车对象*/static class Car {private Framework framework;public Car(int size) {framework = new Framework(size);System.out.println("Car init...");}public void run() {System.out.println("Car run...");}}/*** 车身类*/static class Framework {private Bottom bottom;public Framework(int size) {bottom = new Bottom(size);System.out.println("Framework init...");}}/*** 底盘类*/static class Bottom {private Tire tire;public Bottom(int size) {this.tire = new Tire(size);System.out.println("Bottom init...");}}/*** 轮胎类*/static class Tire {// 尺寸private int size;public Tire(int size) {this.size = size;System.out.println("轮胎尺寸: " + size);}}
}

从以上代码可以看出:当最底层代码改动之后,整个调用链上的所有代码都需要修改(程序的耦合度非常高)。

3. 解决方案

我们尝试换一种思路,我们先设计汽车的大概样子,然后根据汽车的样子来设计车身,根据车身来设计底盘,最后根据底盘来设计轮子。这时候,依赖关系就倒置过来了:轮子依赖底盘,底盘依赖车身,车身依赖汽车。

如何来实现呢:
我们可以尝试不在每个类中自己创建下级类,如果自己创建下级类就会出现当下级类发生改变操作,自己也要跟着修改。此时,我们只需要将原来由自己创建的下级类,改为传递的方式(也就是注入的方式),因为我们不需要在当前类中创建下级类了,所以下级类即使发生变化(创建或减少参数),当前类本身也无需修改任何代码,这样就完成了程序的解耦。

4. IoC程序开发

基于以上思路,我们把调用汽车的程序示例改造一下,把创建子类的方式,改为注入传递的方式。

具体实现代码如下:

public class IocCarExample {public static void main(String[] args) {Tire tire = new Tire(20);Bottom bottom = new Bottom(tire);Framework framework = new Framework(bottom);Car car = new Car(framework);car.run();}static class Car {private Framework framework;public Car(Framework framework) {this.framework = framework;System.out.println("Car init....");}public void run() {System.out.println("Car run...");}}static class Framework {private Bottom bottom;public Framework(Bottom bottom) {this.bottom = bottom;System.out.println("Framework init...");}}static class Bottom {private Tire tire;public Bottom(Tire tire) {this.tire = tire;System.out.println("Bottom init...");}}static class Tire {private int size;public Tire(int size) {this.size = size;System.out.println("轮胎尺寸: " + size);}}
}

代码经过以上调整,无论底层类如何变化,整个调用链是不用做任何改变的,这样就完成了代码之间的解耦,从而实现了更加灵活、通用的程序设计了。

5. IoC优势

  • 降低耦合度:组件不直接依赖具体实现。
  • 增强可测试性:便于模拟依赖进行单元测试。
  • 提高可维护性:依赖关系集中管理,修改时影响小。
  • 灵活性:运行时动态替换依赖实现。

Spring就是一种IoC容器,帮助我们来做了这些资源管理。

二、IoC详解

1. Bean的存储

Spring框架为了更好的服务web应用程序,提供了更丰富的注解。

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

  1. 类注解:@Controller、@Service、@Repository、@Component、@Configuration
  2. 方法注解:@Bean
1.1 @Controller(控制器存储)

使用@Controller存储bean的代码如下所示:

import org.springframework.stereotype.Controller;
@Controller
public class HelloController {public void print() {System.out.println("Hello control");}
}

从Spring容器中获取这个对象:

@SpringBootApplication
public class SpringIocDemoApplication {public static void main(String[] args) {//获取Spring上下文对象ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);//从Spring上下文中获取对象HelloController bean = context.getBean(HelloController.class);//使用对象bean.print();}
}

ApplicationContext:Spring上下文

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

观察运行结果,发现成功从Spring中获取到Controller对象,并执行Controller的print方法。

若把@Controller删掉,观察运行结果:

报错信息显示:找不到bean。

ApplicationContext也提供了其他获取bean的方式,ApplicationContext获取bean对象的功能,是父类BeanFactory提供的功能。

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package org.springframework.beans.factory;
import org.springframework.beans.BeansException;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable;
public interface BeanFactory {String FACTORY_BEAN_PREFIX = "&";char FACTORY_BEAN_PREFIX_CHAR = '&';//1Object getBean(String name) throws BeansException;//2 T getBean(String name, Class requiredType) throws BeansException;//3Object getBean(String name, Object... args) throws BeansException;//4 T getBean(Class requiredType) throws BeansException;//5 T getBean(Class requiredType, Object... args) throws BeansException;//6 ObjectProvider getBeanProvider(Class requiredType);//7 ObjectProvider getBeanProvider(ResolvableType requiredType);boolean containsBean(String name);boolean isSingleton(String name) throws NoSuchBeanDefinitionException;boolean isPrototype(String name) throws NoSuchBeanDefinitionException;boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;boolean isTypeMatch(String name, Class typeToMatch) throws NoSuchBeanDefinitionException;@NullableClass getType(String name) throws NoSuchBeanDefinitionException;@NullableClass getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException;String[] getAliases(String name);
}

常用的是上述1,2,4种,这三种方式获取到的bean是一样的。其中1,2种都涉及到根据名称来获取对象,Bean的名称可以参考官方文档。

程序开发人员不需要为bean指定名称(Beanld),如果没有显式的提供名称(Beanld),Spring容器将为该bean生成唯一的名称。

命名约定使用Java标准约定作为实例字段名。也就是说,bean名称以小写字母开头,然后使用驼峰式大小写。

比如
类名:UserController,Bean的名称为:userController
类名:AccountManager,Bean的名称为:accountManager
类名:AccountService,Bean的名称为:accountService

也有一些特殊情况,当有多个字符并且第一个和第二个字符都是大写时,将保留原始的大小写。这些规则与java.beans.Introspector.decapitalize(Spring在这里使用的)定义的规则相同。

比如
类名:UController,Bean的名称为:UController
类名:AManager,Bean的名称为:AManager

根据这个命名规则,我们来获取bean。

@SpringBootApplication
public class SpringIocDemoApplication {public static void main(String[] args) {ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);HelloController bean = context.getBean(HelloController.class);bean.print();HelloController bean2 = (HelloController) context.getBean("helloController");bean2.print();HelloController bean3 = context.getBean("helloController", HelloController.class);bean3.print();System.out.println(bean);System.out.println(bean2);System.out.println(bean3);}
}

三种方法都成功从Spring中获取到Controller对象,并执行Controller的print方法。并且地址一样,说明是同一个对象。

ApplicationContext VS BeanFactory

  • 从继承关系和功能方面来说:Spring容器有两个顶级的接口:BeanFactory和ApplicationContext。其中BeanFactory提供了基础的访问容器的能力,而ApplicationContext属于BeanFactory的子类,它除了继承了BeanFactory的所有功能之外,它还拥有独特的特性,还添加了对国际化支持、资源访问支持、以及事件传播等方面的支持。
  • 从性能方面来说:ApplicationContext是一次性加载并初始化所有的Bean对象,而BeanFactory是需要那个才去加载那个,因此更加轻量。(空间换时间)
1.2 @Service(服务存储)

使用@Service存储的代码如下所示:

import org.springframework.stereotype.Service;
@Service
public class UserService {public void print() {System.out.println("Hello service");}
}

读取bean的代码:

@SpringBootApplication
public class SpringIocDemoApplication {public static void main(String[] args) {ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);UserService bean = context.getBean(UserService.class);bean.print();}
}

1.3 @Repository(仓库存储)

使用@Repository存储的代码如下所示:

import org.springframework.stereotype.Repository;
@Repository
public class UserRepository {public void print() {System.out.println("Hello Repository");}
}

读取bean的代码:

@SpringBootApplication
public class SpringIocDemoApplication {public static void main(String[] args) {ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);UserRepository bean = context.getBean(UserRepository.class);bean.print();}
}

1.4 @Component(组件存储)

使用@Component存储bean的代码如下所示:

import org.springframework.stereotype.Component;
@Component
public class UserComponent {public void print() {System.out.println("Hello component");}
}

读取bean的代码:

@SpringBootApplication
public class SpringIocDemoApplication {public static void main(String[] args) {ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);UserComponent bean = context.getBean(UserComponent.class);bean.print();}
}

1.5 @Configuration(配置存储)

使用@Configuration存储bean的代码如下所示:

import org.springframework.context.annotation.Configuration;
@Configuration
public class UserConfig {public void print() {System.out.println("Hello configuration");}
}

读取bean的代码:

@SpringBootApplication
public class SpringIocDemoApplication {public static void main(String[] args) {ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);UserConfig bean = context.getBean(UserConfig.class);bean.print();}
}

2. 为什么需要这么多注解?

这个和应用分层是呼应的,让程序员看到类注解之后,就能直接了解当前类的用途。

  • @Controller:控制层,接收请求,对请求进行处理,并进行响应
  • @Servie:业务逻辑层,处理具体的业务逻辑
  • @Repository:数据访问层,也称为持久层.负责数据访问操作
  • @Configuration:配置层.处理项目中的一些配置信息

程序的应用分层,调用流程如下:

查看@Controller /@Service /@Repository /@configuration等注解的源码发现:

其实这些注解里面都有一个注解@Component,说明它们本身就是属于@Component  的"子类"。@Component是一个元注解,也就是说可以注解其他类注解,如@Controller,@Service,@Repository等。这些注解被称为@Component的衍生注解。

@Controller,@Service和@Repository用于更具体的用例(分别在控制层,业务逻辑层,持久化层),在开发过程中,如果你要在业务逻辑层使用@Component或@Service,显然@Service是更好的选择。

3. 方法注解@Bean

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

  1. 使用外部包里的类,没办法添加类注解
  2. 一个类,需要多个对象,比如多个数据源

这种场景,我们就需要使用方法注解@Bean。

下来看方法注解如何使用,创建一个Student类:

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Student {private String name;private Integer age;
}
public class StudentComponent {@Beanpublic Student student() {return new Student("zhangsan", 18);}
}

尝试获取Bean对象中的student:

@SpringBootApplication
public class SpringIocDemoApplication {public static void main(String[] args) {ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);Student student = context.getBean(Student.class);System.out.println(student);}
}

但是未能成功获取。

3.1 方法注解要配合类注解使用

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

@Component
public class StudentComponent {@Beanpublic Student student() {return new Student("zhangsan", 18);}
}

再次运行:

3.2 定义多个对象

对于同一个类,如何定义多个对象呢?(比如多数据源的场景,类是同一个,但是配置不同,志向不同的数据源)

@Bean的使用:

@Component
public class StudentComponent {@Beanpublic Student student1() {return new Student("zhangsan", 18);}@Beanpublic Student student2 () {return new Student("lisi", 20);}
}

根据类型获取到的是哪个对象呢?

@SpringBootApplication
public class SpringIocDemoApplication {public static void main(String[] args) {ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);Student student = context.getBean(Student.class);System.out.println(student);}
}

报错信息显示:期望只有一个匹配,结果发现了两个。

从报错信息中,可以看出@Bean注解的bean,bean的名称就是它的方法名。

接下来我们根据名称来获取bean对象:

@SpringBootApplication
public class SpringIocDemoApplication {public static void main(String[] args) {ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);//根据Bean名称,从Spring上下文获取对象Student student1 = (Student) context.getBean("student1");Student student2 = (Student) context.getBean("student2");System.out.println(student1);System.out.println(student2);}
}

3.3 重命名Bean

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

@Bean(name = {"s","stu"})
public Student student() {return new Student("zhangsan", 18);
}

此时我们使用s或stu就可以获取到Student对象了:

@SpringBootApplication
public class SpringIocDemoApplication {public static void main(String[] args) {ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);Student student1 = (Student) context.getBean("s");Student student2 = (Student) context.getBean("stu");System.out.println(student1);System.out.println(student2);}
}

“name=”可以省略:

@Bean({"s","stu"})
public Student student() {return new Student("zhangsan", 18);
}

只有一个名称时,“{}”也可以省略:

@Bean("s")
public Student student() {return new Student("zhangsan", 18);
}
3.4 扫描路径

下面我们先修改启动类的代码:

@SpringBootApplication
public class SpringIocDemoApplication {public static void main(String[] args) {ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);Student bean = (Student) context.getBean("s");System.out.println(bean);}
}

再修改项目工程的目录结构,测试bean对象是否生效:

使用五大注解声明的bean,要想生效,还需要配置扫描路径,让Spring扫描到这些注解,也就是通过@ComponentScan来配置扫描路径。

@ComponentScan("com.bite.springiocdemo")
@SpringBootApplication
public class SpringIocDemoApplication {public static void main(String[] args) {ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);Student bean = (Student) context.getBean("s");System.out.println(bean);}
}

在之前的代码中,@ComponentScan注解虽然没有显式配置,但是实际上已经包含在了启动类声明注解@SpringBootApplication中了,默认扫描的范围是SpringBoot启动类所在包及其子包。在配置类上添加@ComponentScan注解,该注解默认会扫描该类所在的包下所有的配置类。


创作不易,给个三连支持一下吧

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/964594.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2025年比较好的非标设备机架产品推荐排行榜单,非标设备机架公司精选实力品牌榜单发布

行业背景与评选标准 随着制造业智能化升级加速,非标设备机架作为工业设备的基础支撑结构,其质量与性能直接影响设备运行的稳定性与使用寿命。当前市场上涌现出一批专注于非标设备机架研发制造的企业,它们凭借专业技…

flask: 实现流式输出数据

一,代码: python from flask import Flask, stream_with_context, Response import timephoto = Blueprint(photo, __name__)@photo.route(/stream) def stream():def generate():for i in range(10):yield f"d…

第四十篇

今天是11月13号,上了体育和数据结构

丽江西林瓶灌装线选充氮还是真空型?

在制药与生物制剂行业,针对易氧化药液的西林瓶灌装工艺,设备选型常面临核心痛点:需求与产品错配、参数筛选混乱、保护气体策略不明确。尤其在高原或湿度波动较大的地区(如丽江),空气含氧量及环境洁净度对灌装稳定…

2025年北京继承官司律师机构实力排行榜新鲜发布,继承律师事务所/北京继承律师哪个好/北京丰台继承律师/北京继承纠纷法律事务所选哪家

专业法律服务市场迎来新格局 随着社会经济发展和民众法律意识提升,遗产继承纠纷案件数量持续增长。北京作为国家政治文化中心,其法律服务市场呈现出专业化、精细化的显著特征。近日,通过对北京市继承官司领域律师机…

辽源适配冻干机西林瓶灌装加塞机推荐

近年来,随着生物医药产业在东北地区的加速布局,辽源市对高精度、高洁净度灌装设备的需求显著提升。尤其在冻干制剂领域,适配冻干机的西林瓶灌装加塞机成为众多药企采购的核心设备之一。此类设备需具备半加塞功能,以…

webclientserver

webclient&server一 rt-thread下的webclient解决方案:librws/nopool/websocket软件包与mongoose WebClient和mongoose用Apache 2.0,librws用MIT,nopoll用开源协议。WebClient:对于大多数物联网设备的HTTP通信需…

C#+WPF?​就是工业上位机,用Python+Qt还

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

2025年市场十大名牌管材生产厂家怎么选择,十大名牌管材源头厂家推荐排行榜单精选优质品牌解析

行业权威榜单发布,助力消费者精准决策 随着建筑行业和家装市场的持续发展,管材作为基础设施的重要组成部分,其质量与性能备受关注。本文基于市场调研数据、产品性能指标及用户反馈,对行业内具有代表性的五家管材生…

2025年目前评价高的供应链云服务商推荐排行榜,供应链云服务商深度剖析助力明智之选

行业洞察:供应链云服务市场迎来高质量发展期 随着数字化转型浪潮持续推进,供应链云管理系统已成为企业提升运营效率、优化资源配置的核心工具。根据最新市场调研数据显示,2025年中国供应链云服务市场规模预计突破千…

Linux 交叉编译(toolchain) ARM aarch64版 tcpreplay

前言全局说明一、说明 环境: ubuntu 18.04二、下载源码: 官方下载源 SourceForge存档: 主下载页面: https://sourceforge.net/projects/tcpreplay/files/包含从早期版本到最新版本的所有发布源码下载: https://gith…

ITR经典案例 | 燕千云携手国内知名软件供应商,AI驱动客户服务流升级

甄云携手燕千云AI-ITR,全面升级其IT运维与客户服务体系:从智能提单到AI客服自动转工单,从上下游协同网络到SLA与自动化驱动的智能派单,服务链路全面加速。新体系让服务更快、更准、更可控,也为甄云打造面向未来的…

彻底解决WPS在扩展屏出现的下拉错位现象

先吐槽,wps的缩放适配真是做的很不好。 找到wps软件的启动程序【ksolaunch.exe】,右键【属性】-【兼容性】-【更改高DPI设置】-【高DPI缩放替代】-选择【系统(增强)】

2025年最新钣金加工厂家综合实力排名,助您轻松选择,钣金加工加工厂睿意达发展迅速,实力雄厚

行业背景分析 随着制造业智能化转型加速,自动化钣金加工市场呈现蓬勃发展态势。据行业数据显示,2024年我国钣金加工市场规模已突破8000亿元,预计2025年将保持10%以上的年增长率。在这一背景下,如何选择具备综合实力…

丽江小药厂适用半自动西林瓶灌装生产线

近年来,随着中小型制药企业对成本控制和生产效率的双重关注,半自动西林瓶灌装设备市场呈现出稳步增长态势。据2024年行业调研数据显示,国内半自动西林瓶灌装机价格区间主要集中在8万至25万元之间,其中10万至15万元…

win7 64位 sp1 最高nvidia gt740 显卡驱动版本

win7 64位 sp1 最高nvidia gt740 显卡驱动版本471.12版本https://www.nvidia.cn/drivers/details/180601/

2025年北京继承官司律师机构推荐,这些律所值得信赖,北京丰台继承律师/北京离婚纠纷/离婚纠纷律师/遗产继承律师事务所维权机构选哪家

专业法律服务机构的综合评估 随着社会经济发展和民众法律意识提升,遗产继承纠纷案件呈现逐年增长趋势。在北京地区,专业处理继承官司的律师服务机构备受关注。本文基于市场调研和公开数据,从专业能力、服务质量、客…

在macOS上高效使用8BitDo机械键盘:超级按键与Karabiner配置指南

本文详细介绍了如何在macOS系统上配置8BitDo机械键盘,包括硬件级编程A/B键、超级按键映射技巧,以及使用Karabiner Elements实现高级键位重映射和应用程序特定功能设置的方法。在macOS上使用8BitDo机械键盘(超级按键…

2025年度PLC控制柜厂家红榜,这些企业获好评如潮,正压通风防爆控制柜/水处理PLC的电气控制柜/供水设备变频控制柜产品找哪家

行业洞察:技术实力与专业服务成关键评价指标 随着工业自动化水平的不断提升,PLC控制柜作为自动化系统的核心设备,其市场需求持续增长。在激烈的市场竞争中,一批技术实力突出、服务质量优良的企业脱颖而出,凭借过硬…