【JavaEE】SpringIoC与SpringDI - 详解

news/2025/9/25 19:14:43/文章来源:https://www.cnblogs.com/ljbguanli/p/19111821

【JavaEE】SpringIoC与SpringDI - 详解

目录

  • 一、IoC与DI
    • 1.1 IoC
    • 1.2 DI
  • 二、IoC与DI的使用
  • 三、IoC详解
    • 3.1 Bean的存储
    • 3.2 @Controller(控制器存储)
    • 3.3 获取Bean对象
    • 3.4 @Service(服务存储)
    • 3.5 @Repository(仓库存储)
    • 3.6 @Component(组件存储)
    • 3.7 @Configuration(配置存储)
    • 3.8 五大注解区别
    • 3.9 ⽅法注解@Bean
  • 四、Spring扫描路径
  • 五、DI详解
    • 5.1属性注入@Autowired
    • 5.2 构造方法注入
    • 5.3 setter方法注入
    • 5.4 @Autowired注解问题及解决

一、IoC与DI

名词解释:

  1. spring是一个装了众多工具对象的IoC容器。
  2. IoC思想:对象交给Spring管理,就是IoC思想。
  3. IoC:Inversion of Control,控制反转。

控制权反转,需要某个对象时, 传统开发模式中需要⾃⼰通过 new 创建对象, 现在不需要再进⾏创建, 把创建对象的任务交给容器(IoC容器. Spring是⼀个IoC容器, 所以有时Spring 也称为Spring容器), 程序中只需要依赖注⼊ (Dependency Injection, DI)就可以了.

1.1 IoC

实现下面的需求:

在传统的实现中,我们将每个模块当成一个类:

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);
}
}
}

但是如上面的代码,如果我们要修改一个参数,会导致整个调用链都跟着修改。

我们为解决上面耦合度过高,可以采取:
把由⾃⼰创建的下级类,改为传递的⽅式(也就是注⼊的⽅式),
每次调整只需要调整对应那个类的代码即可。
这样⽆论底层类如何变化,整个调⽤链是不⽤做任何改变的。

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);
}
}
}

1.2 DI

DI: Dependency Injection(依赖注⼊)
容器在运⾏期间, 动态的为应⽤程序提供运⾏时所依赖的资源,称之为依赖注⼊。

就像上面调用关系中:

二、IoC与DI的使用

Spring 是⼀个 IoC(控制反转)容器,作为容器, 那么它就具备两个最基础的功能:
• 存
• 取
Spring 容器 管理的主要是对象, 这些对象, 我们称之为"Bean". 我们把这些对象交由Spring管理, 由 Spring来负责对象的创建和销毁. 我们程序只需要告诉Spring, 哪些需要存, 以及如何从Spring中取出对象

我们实现这样的功能,主要靠两个注解:

  1. Service层及Dao层的实现类,交给Spring管理: 使⽤注解: @Component
  2. 在Controller层 和Service层 注⼊运⾏时依赖的对象: 使⽤注解 @Autowired

像把前面的图书管理系统的BookController重构。
BookController类:

package com.example.project.controller;
import com.example.project.model.BookInfo;
import com.example.project.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RequestMapping("/book")
@RestController
@Component
public class BookController
{
@Autowired
private BookService bookService;
@RequestMapping("/getList")
public List<
BookInfo> getList() {
return bookService.getList();
}
}

BookService类:

package com.example.project.service;
import com.example.project.dao.BookDao;
import com.example.project.model.BookInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
@Component
public class BookService
{
@Autowired
BookDao bookDao ;
public List<
BookInfo> getList() {
List<
BookInfo> books = new ArrayList<
>();
books = bookDao.mockData();
for (BookInfo book:
books) {
if(book.getStatus() == 1) {
book.setStatusCN("可借阅");
} else {
book.setStatusCN("不可借阅");
}
}
return books;
}
}

BookDao类:

package com.example.project.dao;
import com.example.project.model.BookInfo;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
@Component
public class BookDao
{
public List<
BookInfo> mockData() {
List<
BookInfo> books = new ArrayList<
>();
for (int i = 0; i <
5; i++) {
BookInfo book = new BookInfo();
book.setId(i);
book.setBookName("书籍" + i);
book.setAuthor("作者" + i);
book.setCount(i * 5 + 3);
book.setPrice(new BigDecimal(new Random().nextInt(100)));
book.setPublish("出版社" + i);
book.setStatus(1);
books.add(book);
}
return books;
}
}

可以看到在类的调用之间,我们是使用的注解,将类作为另一个类的成员。不用自己去new实例。

三、IoC详解

3.1 Bean的存储

Bean在上面我们也说了,就是Spring管理起来的对象。

实现将对象交给Spring管理,
共有两类注解类型可以:

  1. 类注解:@Controller、@Service、@Repository、@Component、@Configuration.
  2. ⽅法注解:@Bean.

3.2 @Controller(控制器存储)

先使用@Controller将类存储:

package com.example.springioc.controller;
import org.springframework.stereotype.Controller;
@Controller
public class UserController
{
public void hello() {
System.out.println("Hello");
}
}

从Spring容器中获取对象:

  1. 先获取Spring上下⽂对象
  2. 从Spring上下⽂中获取对象
package com.example.springioc.controller;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
@SpringBootApplication
public class SpringIocDemoApplication
{
public static void main(String[] args) {
//先获取Spring上下⽂对象 
ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class,args)
;
//从Spring上下⽂中获取对象
UserController userController = context.getBean(UserController.class)
;
userController.hello();
}
}

3.3 获取Bean对象

获取Bean对象主要是ApplicationContext 类下的getBean方法,有下图中重载。

使用五大类注解让Spring管理Bean对象的默认取名方式如下官方文档:

  1. 将类名转换为小驼峰形式。UserController -》 userController
  2. 当前面是两个即多个大写字母连在一起,Bean对象名就是类名。USController -》 USController

Bean对象名也可以使用注解指定名称,在使用五大注解加上括号即可。栗子: @Controller("name")
使用如下:

package com.example.springioc.controller;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
@SpringBootApplication
public class SpringIocDemoApplication
{
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class,args)
;
UserController bean1 = context.getBean(UserController.class)
;
bean1.hello();
UserController bean2 = (UserController) context.getBean("userController");
bean2.hello();
UserController bean3 = context.getBean("userController", UserController.class)
;
bean3.hello();
}
}

3.4 @Service(服务存储)

使用就加上@Service注解,拿到Bean对象方法不变。

package com.example.springioc.service;
import org.springframework.stereotype.Service;
@Service
public class UserService
{
void print() {
System.out.println("do Service");
}
}

3.5 @Repository(仓库存储)

使用就加上@Repository 注解,拿到Bean对象方法不变。

package com.example.springioc.service;
import org.springframework.stereotype.Repository;
@Repository
public class UserRepository
{
void print() {
System.out.println("do Repository");
}
}

3.6 @Component(组件存储)

使用就加上@Component 注解,拿到Bean对象方法不变。

package com.example.springioc.service;
import org.springframework.stereotype.Component;
@Component
public class UserComponent
{
void print() {
System.out.println("do Component");
}
}

3.7 @Configuration(配置存储)

使用就加上@Configuration注解,拿到Bean对象方法不变。

package com.example.springioc.service;
import org.springframework.context.annotation.Configuration;
@Configuration
public class UserConfiguration
{
void print() {
System.out.println("do Configuration");
}
}

3.8 五大注解区别

@Controller @Service @Repository @Configuration这四个注解都是@Component注解的衍生注解。

分这么多注解就是为了更好地分层(边界在使用中也没非常清晰):

  1. @Controller代表控制层。接收参数返回响应,控制层一定要使用@Controller
  2. @Service代表服务层
  3. @Repository代表数据层
  4. @Configuration代表配置层
  5. @Component代表组件层

3.9 ⽅法注解@Bean

使用:

package com.example.springioc.controller;
import com.example.springioc.model.User;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Controller;
@Controller
public class UserController
{
@Bean
public User user() {
return new User("zhangsan",11);
}
public void hello() {
System.out.println("Hello");
}
}
package com.example.springioc.controller;
import com.example.springioc.model.User;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
@SpringBootApplication
public class SpringIocDemoApplication
{
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class,args)
;
User bean1 = (User) context.getBean("user");
System.out.println(bean1.getName());
}
}

注意事项:

  1. 使用@Bean注解默认方法名就是管理的Bean对象名。
  2. @Bean对象重命名可以直接加括号@Bean("name1"),还可以使用name属性@Bean(name = "name1"),还可以使用value属性@Bean(value = "name1"),并且可以传String数组。
  3. @Bean注解必须搭配五大类注解使用。
  4. 当方法有参数的时候,Spring会从容器中根据参数类型去找,是否有这个类型的对象,如果没有,或者有多个不唯一都会报错,有唯一一个就会拿这个对象赋值。

四、Spring扫描路径

Spring默认的扫描路径是启动类所在路径及其子路径。
当我们要扫描其它路径的时候,可以使用注解@ComponentScan("需要扫描路径"),可以传数组。
其实不怎么用这个注解,直接启动类放在所有需要扫描的路径的最上层包下即可。

五、DI详解

依赖注⼊是⼀个过程,是指IoC容器在创建Bean时, 去提供运⾏时所依赖的资源,⽽资源指的就是对象。

依赖注⼊, Spring给我们提供了三种⽅式:

  1. 属性注⼊(Field Injection)
  2. 构造⽅法注⼊(Constructor Injection)
  3. Setter 注⼊(Setter Injection)

5.1属性注入@Autowired

属性注⼊是使⽤ @Autowired 注解实现的
注意事项:

使用:

package com.example.springioc.controller;
import com.example.springioc.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class UserController
{
@Autowired
private UserService us;
public void hello() {
System.out.println("Hello");
us.print();
}
}
package com.example.springioc;
import com.example.springioc.controller.UserController;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
@SpringBootApplication
public class SpringIocDemoApplication
{
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class,args)
;
UserController bean = context.getBean(UserController.class)
;
bean.hello();
}
}

打印结果为
Hello
do Service、

5.2 构造方法注入

直接使用构造函数,将上面代码改成如下也可以使用。

package com.example.springioc.controller;
import com.example.springioc.service.UserService;
import org.springframework.stereotype.Controller;
@Controller
public class UserController
{
private UserService us;
public UserController(UserService us) {
this.userService = us;
}
public void hello() {
System.out.println("Hello");
us.print();
}
}

注意事项:

  1. 当只有一个构造函数的时候,直接可以注入。
  2. 当有两个及以上构造函数的时候,Spring无法辨别使用哪一个构造函数注入,需要在使用的构造函数前加上@Autowired注解。
  3. 只能在一个构造方法上加上@Autowired注解。

5.3 setter方法注入

直接加上set方法,加上@Autowired注解,将上面代码改成如下也可以使用。

package com.example.springioc.controller;
import com.example.springioc.service.UserService;
import org.springframework.stereotype.Controller;
@Controller
public class UserController
{
private UserService us;
@Autowired
public void setUserService(UserService us) {
this.us = us;
}
public void hello() {
System.out.println("Hello");
us.print();
}
}

注意事项:

  • set方法必须加上@Autowired注解,可以给多个set方法使用注解。
  • 不能修饰final修饰的成员的set方法。

优缺点比较:

  1. 属性注⼊
    • 优点:简洁,使⽤⽅便;
    • 缺点:
      • 只能⽤于 IoC 容器,如果是⾮ IoC 容器不可⽤,并且只有在使⽤的时候才会出现NPE(空指针异常)
      • 不能注⼊⼀个Final修饰的属性
  1. 构造函数注⼊(Spring 4.X推荐)
    • 优点:
      • 可以注⼊final修饰的属性
      • 注⼊的对象不会被修改
      • 依赖对象在使⽤前⼀定会被完全初始化,因为依赖是在类的构造⽅法中执⾏的,⽽构造⽅法是在类加载阶段就会执⾏的⽅法.
      • 通⽤性好,构造⽅法是JDK⽀持的, 所以更换任何框架,他都是适⽤的
    • 缺点:
      • 注⼊多个对象时, 代码会⽐较繁琐
  1. Setter注⼊(Spring 3.X推荐)
    • 优点:⽅便在类实例之后, 重新对该对象进⾏配置或者注⼊
    • 缺点:
      • 不能注⼊⼀个Final修饰的属性
      • 注⼊对象可能会被改变, 因为setter⽅法可能会被多次调⽤,就有被修改的⻛险

5.4 @Autowired注解问题及解决

当一个类交给Spring多个对象后,使用@Autowired注解,会无法分辨。

package com.example.springioc.service;
import com.example.springioc.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;
@Service
public class UserService
{
@Bean
public User u1(String name) {
return new User(name,11);
}
@Bean
public User u2() {
return new User("lisi",18);
}
@Bean
public String name () {
return "zhangsan";
}
public void print() {
System.out.println("do Service");
}
}
package com.example.springioc.controller;
import com.example.springioc.model.User;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Controller;
@Controller
public class UserController
{
@Resource(name = "u1")
private User user;
public void hello() {
System.out.println("Hello");
System.out.println(user.toString());
}
}

报错信息:

解决方法:
提供了以下⼏种注解解决:

  • @Primary
  • @Qualifier
  • @Resource
  1. 使⽤@Primary注解:当存在多个相同类型的Bean注⼊时,加上@Primary注解,来确定默认的实现。例如上面代码:
@Bean
@Primary
public String name () {
return "zhangsan";
}
  1. 使⽤@Qualifier注解:指定当前要注⼊的bean对象。在@Qualifier的value属性中,指定注⼊的bean的名称,必须与@Autowired一起用。例如上面代码:
@Autowired
@Qualifier("u1")
private User user;
  1. 使⽤@Resource注解:是按照bean的名称进⾏注⼊。通过name属性指定要注⼊的bean的名称。@Resource是JDK提供的注解。
    例如上面代码:
@Resource(name = "u2")
private User user;

@Autowired工作流程

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

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

相关文章

24.Linux硬盘分区管理 - 详解

24.Linux硬盘分区管理 - 详解pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco"…

CCF CSP-J 2025_from_黄老师_km

下面把 2025 CCF CSP-J 第一轮(入门级)C++ 试题 中 所有可辨认的选择 / 判断 / 填空 按题号逐一给出:正确答案 极简解析(why) 易错点 / 知识彩蛋【单选题】(每题 2 分,共 30 分)题号 答案 秒懂解析1 D 科学计数…

个人cms网站凡科做的网站打不开

面向对象和面向过程的区别&#xff1f; 面向对象编程&#xff08;OOP&#xff09;和面向过程编程&#xff08;POP&#xff09;是两种不同的编程范式&#xff0c;它们之间有一些重要的区别&#xff1a; 思想方式&#xff1a; 面向对象编程&#xff1a;将问题看作是一组对象之间…

网站flash代码成立公司一年需要多少费用

目录简介数据手册接口简单 I/OXBus简单 I/O 对比 XBus语言参考程序结构注释标签寄存器accdatp0、p1、x0、x1、x2、x3null 伪寄存器指令操作数确保进行足够的睡眠 (slp)&#xff01;基本指令算法指令条件指令隐藏指令游戏界面DIY版本&#xff1a; 简介 以下介绍摘自未来软件园 …

AI一周资讯 250918-250925

原文: https://mp.weixin.qq.com/s/6_sSbUDYOujOjeF-n1rnGA 行业首个“高刷”视频理解多模态模型!MiniCPM-V 4.5凭三大技术成30B以下最优开源 本周,由清华大学自然语言处理实验室和面壁智能联合开发的MiniCPM-V 4.5亮…

深入解析:SpringBoot与反射

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

云栖小镇现场追踪!触摸AI 未来

原文: https://mp.weixin.qq.com/s/-jkeIywNb1alCajgZveSSw 9 月 24 日的杭州云栖小镇,人山人海 ——2025 云栖大会。作为每年必蹲的科技盛会,今年大会的主题 “云智一体・碳硅共生” 可不是空口号。走在 4 万平方米…

刚入手一手房怎么网上做网站Wordpress如何创建菜单

edge浏览器扩展插件中心10月发布 可直接安装Chrome扩展 Windows 10的全新浏览器Edge收获了不少好评&#xff0c;我们也知道它将在今年秋天迎来扩展程序的支持。 Mashable已经指出&#xff0c;微软将保证Edge上的扩展严控权限、卸载干净&#xff0c;保证不拖累整体性能&#x…

济南网站建设seo优化wordpress页面内

文章目录 python常用库之数据库orm框架之SQLAlchemy一、什么是SQLAlchemySQLAlchemy 使用场景 二、SQLAlchemy使用SQLAlchemy根据模型查询SQLAlchemy SQL 格式化的方式db_session.query和 db_session.execute区别实测demo 总结&#xff1a;让我们留意一下SQLAlchemy 的 lazy lo…

AT_arc154_d [ARC154D] A + B C ?

直接被这个题闪到了。 首先发现 \(1\) 是最小的,其有很多性质,因此可以花费 \(n - 1\) 次操作比较出来 \(1\) 的位置。 同理,\(2, 3, ..., n\) 都是可以这样比较出来的,但操作次数是 \(O(n^2)\) 级别的,题目只给了…

SQL注入-联合注入

一、SQL语句基础知识 首先打开PHP的数据库,如下图所示,再在终端中输入后连接到数据库点击查看代码 mysql -u root -p1.1 MySQL的基础语句 1.1.1 创造数据库 用来创建一个数据库的语句如下:点击查看代码 create data…

实用指南:【JavaEE】多线程案例(一)

实用指南:【JavaEE】多线程案例(一)pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Mon…

网站开发和c语言反网站搭建一条龙

MySQL 5.5以上版本 与之前的版本安装出入有些区别&#xff1a; 下面是安装过程mysql5.6 下载地址&#xff1a;ftp://mirror.switch.ch/mirror/mysql/Downloads/MySQL-5.6/一&#xff1a;卸载旧版本使用下面的命令检查是否安装有MySQL Serverrpm -qa | grep mysql有的话通过下面…

架构架构设计师备考第32天——数据库交互NoSQL

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

交互:在终端中输入用户信息

交互:在终端中输入用户信息Python可以允许用户在终端中输入一些信息。 Input功能 接受输入字符串 # -*- coding: utf-8 -*- name = input("Please input your name:") print("Hello " + name + &…

传奇三端互通新开服网站百度非企推广开户

这个算法来自LINUX的源码&#xff0c;下面带有大神的解析&#xff0c;自己在RTC实验中也使用了&#xff0c;不用月份表&#xff0c;润平年的处理&#xff0c;几行就可得出结果&#xff0c;以下是程序和大神的解析Linux源码中的mktime算法解析我们知道&#xff0c;从CMOS中读出来…

php网站开发系统wordpress 引用网页

近日&#xff0c;紫光云技术有限公司在天津举行主题为“产业城市 擎领未来”的IMPACT2019紫光云峰会&#xff0c;深度阐释打造产业数字引擎的理念和实践&#xff0c;并为unI X云创中心揭牌&#xff0c;发布紫光云芯片产业数字引擎。 天津市人民政府副秘书长杨明远为大会致辞会上…

爱站网关键字挖掘wordpress 小组

Java中new一个对象时&#xff0c;JVM到底做了什么&#xff1f; 在Java编程中&#xff0c;new关键字是我们创建对象的最常用方式。但你是否想过&#xff0c;当你写下new MyClass()时&#xff0c;Java虚拟机&#xff08;JVM&#xff09;到底在背后做了哪些工作&#xff1f;今天&…

电脑迁移技巧:适用于 Windows 10/11 的免费磁盘克隆优秀的工具

电脑迁移技巧:适用于 Windows 10/11 的免费磁盘克隆优秀的工具pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Co…

详细介绍:Windows安装PostgreSQL入门操作手册

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