Spring實現(xiàn)IoC和DI的方法詳解
一.Spring中的IoC
IoC全稱Inversion of Control (控制反轉) ,這里的控制其實是控制權的意思。可以理解為對象的獲取權力和方式發(fā)生了發(fā)轉。也就是說,當需要某個對象時,傳統(tǒng)開發(fā)模式中需要??通過 new 創(chuàng)建對象;而IoC的思想則不一樣,IoC旨在把創(chuàng)建對象的任務交給外部的容器,程序中只需要引入容器里存放的需要的對象即可。這個容器一般被稱為:IoC容器。
其實IoC我們在前?已經(jīng)使?了,我們在前?講到,在類上?添加 @RestController 和 @Controller 注解,就是把這個對象交給Spring管理,Spring 框架啟動時就會加載該類,把對象交給Spring管理,這就是Spring中的IoC思想。Spring對于IoC的實現(xiàn)主要是通過工廠設計模式+反射來實現(xiàn)的,當我們需要某個對象的時候,只需要將創(chuàng)建對象的任務交給容器,在程序中只需要調用(注入)即可。
前?我們提到IoC控制反轉,就是將對象的控制權交給Spring的IOC容器,由IOC容器創(chuàng)建及管理對象。那么在Spring程序中,我們該如何通過代碼來實現(xiàn)IoC呢?
Spring框架為了更好的服務應用程序,提供了倆類注解來實現(xiàn)將對象集中管理創(chuàng)建:
- 類注解:@Controller、@Service、@Repository、@Component、@Configuration
- 方法注解:@Bean
@Controller
使? @Controller 存儲 bean 的代碼如下所?:
@Controller // 將對象存儲到 Spring 中
public class UserController {
public void sayHi(){
System.out.println("hi,UserController...");
}
}如何觀察這個對象已經(jīng)存在Spring容器當中了呢? 我們可以通過啟動類中的getBean方法來得到Spring中管理的Bean。
@SpringBootApplication
public class IoCdemoApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(IoCdemoApplication.class, args);
UserController bean = context.getBean(UserController.class);
bean.sayHi();
}
}在控制臺即可觀察到輸出:

但是一旦將@Controller注釋掉,程序就會報錯找不到這個Bean,這就說明了使用@Controller確實是可以將該類對象交給Spring進行管理

@Service
@Service
public class UserService {
public void sayHi(String name) {
System.out.println("Hi," + name);
}
}還是來獲取一下這個對象,看看結果如何
ConfigurableApplicationContext context = SpringApplication.run(IoCdemoApplication.class, args);
UserService bean = context.getBean(UserService.class);
bean.sayHi("CSDN");
如果注釋掉@Service就會報錯,因此可以證實@Service可以將類對象交給Spring進行管理

后續(xù)的三個注解如果進行相同方式的驗證都會得到一樣的結果,這里就不再贅述
@Repository
@Repository
public class UserRepository {
public void sayHi() {
System.out.println("Hi, UserRepository~");
}
}@Component
@Component
public class UserComponent {
public void sayHi() {
System.out.println("Hi, UserComponent~");
}
}@Configuration
@Configuration
public class UserConfiguration {
public void sayHi() {
System.out.println("Hi,UserConfiguration~");
}
}那么既然這么多注解干的都是同一件事,為什么還非要分出這么多注解呢?
類注解之間的區(qū)別
這個也是和咱們前?講的應?分層是呼應的,不同的注解標識著不同的信息,這些注解可以讓程序員看到類注解之后,就能直接了解當前類的?途。
- @Controller:控制層, 接收請求, 對請求進?處理, 并進?響應
- @Servie:業(yè)務邏輯層, 處理具體的業(yè)務邏輯
- @Repository:數(shù)據(jù)訪問層,也稱為持久層. 負責數(shù)據(jù)訪問操作
- @Configuration:配置層. 處理項?中的?些配置信息
這和每個省/市都有??的?牌號是?樣的。?牌號都是唯?的,標識?個?輛的。但是為什么還需要設置不同的?牌開頭呢??如陜西的?牌號就是:陜X:XXXXXX,北京的?牌號:京X:XXXXXX,甚??個省不同的縣區(qū)也是不同的,?如西安就是,陜A:XXXXX,咸陽:陜B:XXXXXX,寶雞,陜C:XXXXXX,?樣。
這樣做的好處除了可以節(jié)約號碼之外,更重要的作?是可以直觀的標識?輛?的歸屬地.
對于一般的開發(fā)我們通過不同的注解去確認它是哪一層的代碼,這樣更方面上下層進行調用

類注解之間的聯(lián)系
細心的朋友可能發(fā)現(xiàn)了,上述的注解中少了一個@Component注解,我們不妨打開每個注解的源碼看看。

我們會發(fā)現(xiàn)上述4個注解中都有@Component注解,這說明它們本?就是屬于 @Component 的 "?類"。@Component 是?個元注解,也就是說可以注解其他類注解,如 @Controller、@Service 、@Repository 等, 這些注解被稱為 @Component 的衍?注解。
@Controller、@Service 和 @Repository ?于更具體的?例(分別在控制層, 業(yè)務邏輯層, 持久化層),在開發(fā)過程中,如果你要在業(yè)務邏輯層使? @Component 或 @Service,顯然@Service是更好的選擇。
諸如@Configuration,見名知意就是用來標記配置相關的,比如我們想用Redis的數(shù)據(jù)庫,在使用Redis之前需要對Redis數(shù)據(jù)庫的密碼或者庫做一些配置,配置好了之后我們需要將這個配置類交給Spring管理方便我們在別的地方直接訪問Redis數(shù)據(jù)庫,這時使用@Configuration就是很好的選擇。
?如杯?有喝?杯、刷?杯等,但是我們更傾向于在?常喝?時使??杯,洗漱時使?刷?杯。
方法注解@Bean
類注解是添加到某個類上的, 但是存在兩個問題
- 使?外部包?的類, 沒辦法添加類注解
- ?個類, 需要多個對象, ?如多個數(shù)據(jù)源
比如我們想通過引入第三方的工具類去操作數(shù)據(jù)庫,我們想將這個類交給Spring管理方便我們進行二次開發(fā),但是由于第三方包只能讀取不能寫入的情況,就會陷入進退倆難的情況。
@Bean 同上述類注解的功能一樣,都是將標記的對象交給Spring進行管理,但在 Spring 框架的設計中,?法注解 @Bean 要配合類注解才能將對象正常的存儲到 Spring 容器中,如下代碼所?:
@Component
public class BeanConfig {
@Bean
public User user(){
User user = new User();
user.setName("zhangsan");
user.setAge(18);
return user;
}
}我們也可以通過設置name屬性給Bean對象進行重命名
@Bean(name = {"u1","user1"})二.DI——Dependency Injection(依賴注入)
依賴注?是?個過程,是指IoC容器在創(chuàng)建Bean時, 去提供運?時所依賴的資源,?資源指的就是對象,在之前程序案例中,我們使?了 @Autowired 這個注解,完成了依賴注?的操作。
關于依賴注?,Spring也給我們提供了三種?式:
- 屬性注?(Field Injection)
- 構造?法注?(Constructor Injection)
- Setter 注?(Setter Injection)
屬性注入
屬性注?是使? @Autowired 實現(xiàn)的,將 Service 類注?到 Controller 類中。
Service 類的實現(xiàn)代碼如下:
import org.springframework.stereotype.Service;
@Service
public class UserService {
public void sayHi() {
System.out.println("Hi,UserService");
}
}Controller 類的實現(xiàn)代碼如下:
@Controller
public class UserController {
//注??法1: 屬性注?
@Autowired
private UserService userService;
public void sayHi() {
System.out.println("hi,UserController...");
userService.sayHi();
}
}構造方法注入
構造?法注?是在類的構造?法中實現(xiàn)注?,如下代碼所?:
@Controller
public class UserController2 {
//注??法2: 構造?法
private UserService userService;
@Autowired
public UserController2(UserService userService) {
this.userService = userService;
}
public void sayHi(){
System.out.println("hi,UserController2...");
userService.sayHi();
}
}如果類只有?個構造?法,那么 @Autowired 注解可以省略;如果類中有多個構造?法,那么需要添加上 @Autowired 來明確指定到底使?哪個構造?法。
Setter注入
Setter 注?和屬性的 Setter ?法實現(xiàn)類似,只不過在設置 set ?法的時候需要加上 @Autowired 注解 ,如下代碼所?:
@Controller
public class UserController3 {
//注??法3: Setter?法注?
private UserService userService;
@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
public void sayHi(){
System.out.println("hi,UserController3...");
userService.sayHi();
}
}三種注入的優(yōu)缺點
屬性注?
優(yōu)點:
- 簡潔,使??便
缺點:
- 只能?于 IoC 容器,如果是? IoC 容器不可?,并且只有在使?的時候才會出現(xiàn) NPE(空指針異常)
- 不能注??個Final修飾的屬性
構造函數(shù)注入(Spring 4.X推薦)
優(yōu)點:
- 可以注?final修飾的屬性
- 注?的對象不會被修改
- 依賴對象在使?前?定會被完全初始化,因為依賴是在類的構造?法中執(zhí)?的,?構造?法是在類加載階段就會執(zhí)?的?法.
- 通?性好, 構造?法是JDK?持的, 所以更換任何框架,他都是適?的
缺點:
- 注?多個對象時, 代碼會?較繁瑣
Setter注入(Spring 3.X推薦)
優(yōu)點:
- ?便在類實例之后, 重新對該對象進?配置或者注?
缺點:
- 不能注??個Final修飾的屬性
- 注?對象可能會被改變, 因為setter?法可能會被多次調?, 就有被修改的?險
@Autowired存在問題
當同?類型存在多個bean時, 使?@Autowired會存在問題
@Component
public class BeanConfig {
@Bean("u1")
public User user1(){
User user = new User();
user.setName("zhangsan");
user.setAge(18);
return user;
}
@Bean
public User user2() {
User user = new User();
user.setName("lisi");
user.setAge(19);
return user;
}
}@Controller
public class UserController {
@Autowired
private UserService userService;
//注?user
@Autowired
private User user;
public void sayHi(){
System.out.println("hi,UserController...");
userService.sayHi();
System.out.println(user);
}
}程序會出現(xiàn)無法正確找到Bean的報錯

如何解決上述問題呢?Spring提供了以下?種解決?案:
- @Primary
- @Qualifier
- @Resource
使?@Primary注解:當存在多個相同類型的Bean注?時,加上@Primary注解,來確定默認的實現(xiàn)
@Component
public class BeanConfig {
@Primary //指定該bean為默認bean的實現(xiàn)
@Bean("u1")
public User user1(){
User user = new User();
user.setName("zhangsan");
user.setAge(18);
return user;
}
@Bean
public User user2() {
User user = new User();
user.setName("lisi");
user.setAge(19);
return user;
}
}使?@Qualifier注解:指定當前要注?的bean對象。 在@Qualifier的value屬性中,指定注?的bean的名稱(@Qualifier注解不能單獨使?,必須配合@Autowired使?)
@Controller
public class UserController {
@Qualifier("user2") //指定bean名稱
@Autowired
private User user;
public void sayHi(){
System.out.println("hi,UserController...");
System.out.println(user);
}
}使?@Resource注解:是按照bean的名稱進?注?。通過name屬性指定要注?的bean的名稱。并且由于@Resource是由JDK提供的,因此在其他框架下也有可延展性。
@Controller
public class UserController {
@Resource(name = "user2")
private User user;
public void sayHi(){
System.out.println("hi,UserController...");
System.out.println(user);
}
}@Autowired 是spring框架提供的注解,?@Resource是JDK提供的注解
@Autowired 默認是按照類型注?,?@Resource是按照名稱注?。相?于 @Autowired 來說 @Resource ?持更多的參數(shù)設置,例如 name 設置,根據(jù)名稱獲取 Bean
以上就是Spring實現(xiàn)IoC和DI的方法詳解的詳細內(nèi)容,更多關于Spring實現(xiàn)IoC和DI的資料請關注腳本之家其它相關文章!
相關文章
Java使用Spire.Presentation for Java庫合并PowerPoint的技術教
在現(xiàn)代企業(yè)和個人開發(fā)中,文檔處理是不可或缺的一環(huán),尤其是在報告演示、內(nèi)容整合等場景下,PowerPoint 文件的自動化處理需求日益增長,本文將深入探討如何利用 Java 編程語言,結合強大的 Spire.Presentation for Java庫,實現(xiàn)PowerPoiont文件的合并2026-01-01
關于使用Mybatisplus自帶的selectById和insert方法時的一些問題
這篇文章主要介紹了關于使用Mybatisplus自帶的selectById和insert方法時的一些問題,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-08-08
為Java應用創(chuàng)建Docker鏡像的3種方式總結
Docker的使用可以將應用程序做成鏡像,這樣可以將鏡像發(fā)布到私有或者公有倉庫中,在其他主機上也可以pull鏡像,并且運行容器,運行程,下面這篇文章主要給大家總結介紹了關于為Java應用創(chuàng)建Docker鏡像的3種方式,需要的朋友可以參考下2023-06-06
在SpringBoot+MyBatis項目中實現(xiàn)MySQL讀寫分離的實戰(zhàn)指南
在Spring Boot和MyBatis項目中實現(xiàn)MySQL讀寫分離,主要有兩種思路:一種是在應用層通過代碼和配置手動控制,另一種是借助中間件自動路由,本文介紹了幾種主流的實現(xiàn)方案,需要的朋友可以參考下2025-11-11
分布式調度XXL-Job整合Springboot2.X實戰(zhàn)操作過程(推薦)
這篇文章主要介紹了分布式調度XXL-Job整合Springboot2.X實戰(zhàn)操作,包括定時任務的使用場景和常見的定時任務,通過本文學習幫助大家該選擇哪個分布式任務調度平臺,對此文感興趣的朋友一起看看吧2022-04-04

