SpringIoC與SpringDI詳解
一、IoC與DI
名詞解釋?zhuān)?/p>
- spring是一個(gè)裝了眾多工具對(duì)象的IoC容器。
- IoC思想:對(duì)象交給Spring管理,就是IoC思想。
- IoC:Inversion of Control,控制反轉(zhuǎn)。
控制權(quán)反轉(zhuǎn),需要某個(gè)對(duì)象時(shí), 傳統(tǒng)開(kāi)發(fā)模式中需要??通過(guò) new 創(chuàng)建對(duì)象, 現(xiàn)在不需要再進(jìn)?創(chuàng)建, 把創(chuàng)建對(duì)象的任務(wù)交給容器(IoC容器. Spring是?個(gè)IoC容器, 所以有時(shí)Spring 也稱(chēng)為Spring容器), 程序中只需要依賴(lài)注? (Dependency Injection, DI)就可以了.
1.1 IoC
實(shí)現(xiàn)下面的需求:
在傳統(tǒng)的實(shí)現(xiàn)中,我們將每個(gè)模塊當(dāng)成一個(gè)類(lèi):
public class NewCarExample { public static void main(String[] args) { Car car = new Car(); car.run(); } /** * 汽?對(duì)象 */ static class Car { private Framework framework; public Car() { framework = new Framework(); System.out.println("Car init...."); } public void run(){ System.out.println("Car run..."); } } /** * ??類(lèi) */ static class Framework { private Bottom bottom; public Framework() { bottom = new Bottom(); System.out.println("Framework init..."); } } /** * 底盤(pán)類(lèi) */ static class Bottom { private Tire tire; public Bottom() { this.tire = new Tire(); System.out.println("Bottom init..."); } } /** * 輪胎類(lèi) */ static class Tire { // 尺? private int size; public Tire(){ this.size = 17; System.out.println("輪胎尺?:" + size); } } }
但是如上面的代碼,如果我們要修改一個(gè)參數(shù),會(huì)導(dǎo)致整個(gè)調(diào)用鏈都跟著修改。
我們?yōu)榻鉀Q上面耦合度過(guò)高,可以采取:
把由??創(chuàng)建的下級(jí)類(lèi),改為傳遞的?式(也就是注?的?式),
每次調(diào)整只需要調(diào)整對(duì)應(yīng)那個(gè)類(lèi)的代碼即可。
這樣?論底層類(lèi)如何變化,整個(gè)調(diào)?鏈?zhǔn)遣?做任何改變的。
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(依賴(lài)注?)
容器在運(yùn)?期間, 動(dòng)態(tài)的為應(yīng)?程序提供運(yùn)?時(shí)所依賴(lài)的資源,稱(chēng)之為依賴(lài)注?。
就像上面調(diào)用關(guān)系中:
二、IoC與DI的使用
Spring 是?個(gè) IoC(控制反轉(zhuǎn))容器,作為容器, 那么它就具備兩個(gè)最基礎(chǔ)的功能:
• 存
• 取
Spring 容器 管理的主要是對(duì)象, 這些對(duì)象, 我們稱(chēng)之為"Bean". 我們把這些對(duì)象交由Spring管理, 由 Spring來(lái)負(fù)責(zé)對(duì)象的創(chuàng)建和銷(xiāo)毀. 我們程序只需要告訴Spring, 哪些需要存, 以及如何從Spring中取出對(duì)象
我們實(shí)現(xiàn)這樣的功能,主要靠?jī)蓚€(gè)注解:
- Service層及Dao層的實(shí)現(xiàn)類(lèi),交給Spring管理: 使?注解: @Component
- 在Controller層 和Service層 注?運(yùn)?時(shí)依賴(lài)的對(duì)象: 使?注解 @Autowired
像把前面的圖書(shū)管理系統(tǒng)的BookController重構(gòu)。
BookController類(lèi):
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類(lèi):
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類(lèi):
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("書(shū)籍" + 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; } }
可以看到在類(lèi)的調(diào)用之間,我們是使用的注解,將類(lèi)作為另一個(gè)類(lèi)的成員。不用自己去new實(shí)例。
三、IoC詳解
3.1 Bean的存儲(chǔ)
Bean在上面我們也說(shuō)了,就是Spring管理起來(lái)的對(duì)象。
實(shí)現(xiàn)將對(duì)象交給Spring管理,
共有兩類(lèi)注解類(lèi)型可以:
- 類(lèi)注解:@Controller、@Service、@Repository、@Component、@Configuration.
- ?法注解:@Bean.
3.2 @Controller(控制器存儲(chǔ))
先使用@Controller將類(lèi)存儲(chǔ):
package com.example.springioc.controller; import org.springframework.stereotype.Controller; @Controller public class UserController { public void hello() { System.out.println("Hello"); } }
從Spring容器中獲取對(duì)象:
先獲取Spring上下?對(duì)象
從Spring上下?中獲取對(duì)象
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上下?對(duì)象 ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class,args); //從Spring上下?中獲取對(duì)象 UserController userController = context.getBean(UserController.class); userController.hello(); } }
3.3 獲取Bean對(duì)象
獲取Bean對(duì)象主要是ApplicationContext 類(lèi)下的getBean方法,有下圖中重載。
使用五大類(lèi)注解讓Spring管理Bean對(duì)象的默認(rèn)取名方式如下官方文檔:
- 將類(lèi)名轉(zhuǎn)換為小駝峰形式。
UserController -》 userController
- 當(dāng)前面是兩個(gè)即多個(gè)大寫(xiě)字母連在一起,Bean對(duì)象名就是類(lèi)名。
USController -》 USController
Bean對(duì)象名也可以使用注解指定名稱(chēng),在使用五大注解加上括號(hào)即可。栗子: @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(服務(wù)存儲(chǔ))
使用就加上@Service注解,拿到Bean對(duì)象方法不變。
package com.example.springioc.service; import org.springframework.stereotype.Service; @Service public class UserService { void print() { System.out.println("do Service"); } }
3.5 @Repository(倉(cāng)庫(kù)存儲(chǔ))
使用就加上@Repository 注解,拿到Bean對(duì)象方法不變。
package com.example.springioc.service; import org.springframework.stereotype.Repository; @Repository public class UserRepository { void print() { System.out.println("do Repository"); } }
3.6 @Component(組件存儲(chǔ))
使用就加上@Component 注解,拿到Bean對(duì)象方法不變。
package com.example.springioc.service; import org.springframework.stereotype.Component; @Component public class UserComponent { void print() { System.out.println("do Component"); } }
3.7 @Configuration(配置存儲(chǔ))
使用就加上@Configuration注解,拿到Bean對(duì)象方法不變。
package com.example.springioc.service; import org.springframework.context.annotation.Configuration; @Configuration public class UserConfiguration { void print() { System.out.println("do Configuration"); } }
3.8 五大注解區(qū)別
@Controller @Service @Repository @Configuration這四個(gè)注解都是@Component注解的衍生注解。
分這么多注解就是為了更好地分層(邊界在使用中也沒(méi)非常清晰):
- @Controller代表控制層。接收參數(shù)返回響應(yīng),控制層一定要使用@Controller
- @Service代表服務(wù)層
- @Repository代表數(shù)據(jù)層
- @Configuration代表配置層
- @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()); } }
注意事項(xiàng):
- 使用@Bean注解默認(rèn)方法名就是管理的Bean對(duì)象名。
- @Bean對(duì)象重命名可以直接加括號(hào)
@Bean("name1")
,還可以使用name屬性@Bean(name = "name1")
,還可以使用value屬性@Bean(value = "name1")
,并且可以傳String數(shù)組。
- @Bean注解必須搭配五大類(lèi)注解使用。
- 當(dāng)方法有參數(shù)的時(shí)候,Spring會(huì)從容器中根據(jù)參數(shù)類(lèi)型去找,是否有這個(gè)類(lèi)型的對(duì)象,如果沒(méi)有,或者有多個(gè)不唯一都會(huì)報(bào)錯(cuò),有唯一一個(gè)就會(huì)拿這個(gè)對(duì)象賦值。
四、Spring掃描路徑
Spring默認(rèn)的掃描路徑是啟動(dòng)類(lèi)所在路徑及其子路徑。
當(dāng)我們要掃描其它路徑的時(shí)候,可以使用注解@ComponentScan("需要掃描路徑")
,可以傳數(shù)組。
其實(shí)不怎么用這個(gè)注解,直接啟動(dòng)類(lèi)放在所有需要掃描的路徑的最上層包下即可。
五、DI詳解
依賴(lài)注?是?個(gè)過(guò)程,是指IoC容器在創(chuàng)建Bean時(shí), 去提供運(yùn)?時(shí)所依賴(lài)的資源,?資源指的就是對(duì)象。
依賴(lài)注?, Spring給我們提供了三種?式:
- 屬性注?(Field Injection)
- 構(gòu)造?法注?(Constructor Injection)
- Setter 注?(Setter Injection)
5.1屬性注入@Autowired
屬性注?是使? @Autowired 注解實(shí)現(xiàn)的
注意事項(xiàng):
- 注入的對(duì)象必須是容器中已經(jīng)有的,也就是使用五大類(lèi)注解交給Spring管理的。
- @Autowired不能修飾final修飾的成員。
使用:
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(); } }
打印結(jié)果為
Hello
do Service、
5.2 構(gòu)造方法注入
直接使用構(gòu)造函數(shù),將上面代碼改成如下也可以使用。
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(); } }
注意事項(xiàng):
- 當(dāng)只有一個(gè)構(gòu)造函數(shù)的時(shí)候,直接可以注入。
- 當(dāng)有兩個(gè)及以上構(gòu)造函數(shù)的時(shí)候,Spring無(wú)法辨別使用哪一個(gè)構(gòu)造函數(shù)注入,需要在使用的構(gòu)造函數(shù)前加上@Autowired注解。
- 只能在一個(gè)構(gòu)造方法上加上@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(); } }
注意事項(xiàng):
- set方法必須加上@Autowired注解,可以給多個(gè)set方法使用注解。
- 不能修飾final修飾的成員的set方法。
優(yōu)缺點(diǎn)比較:
- 屬性注?
- 優(yōu)點(diǎn):簡(jiǎn)潔,使??便;
- 缺點(diǎn):
- 只能?于 IoC 容器,如果是? IoC 容器不可?,并且只有在使?的時(shí)候才會(huì)出現(xiàn)NPE(空指針異常)
- 不能注??個(gè)Final修飾的屬性
- 構(gòu)造函數(shù)注?(Spring 4.X推薦)
- 優(yōu)點(diǎn):
- 可以注?final修飾的屬性
- 注?的對(duì)象不會(huì)被修改
- 依賴(lài)對(duì)象在使?前?定會(huì)被完全初始化,因?yàn)橐蕾?lài)是在類(lèi)的構(gòu)造?法中執(zhí)?的,?構(gòu)造?法是在類(lèi)加載階段就會(huì)執(zhí)?的?法.
- 通?性好,構(gòu)造?法是JDK?持的, 所以更換任何框架,他都是適?的
- 缺點(diǎn):
- 注?多個(gè)對(duì)象時(shí), 代碼會(huì)?較繁瑣
- Setter注?(Spring 3.X推薦)
- 優(yōu)點(diǎn):?便在類(lèi)實(shí)例之后, 重新對(duì)該對(duì)象進(jìn)?配置或者注?
- 缺點(diǎn):
- 不能注??個(gè)Final修飾的屬性
- 注?對(duì)象可能會(huì)被改變, 因?yàn)閟etter?法可能會(huì)被多次調(diào)?,就有被修改的?險(xiǎn)
5.4 @Autowired注解問(wèn)題及解決
當(dāng)一個(gè)類(lèi)交給Spring多個(gè)對(duì)象后,使用@Autowired注解,會(huì)無(wú)法分辨。
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()); } }
報(bào)錯(cuò)信息:
解決方法:
提供了以下?種注解解決:
- @Primary
- @Qualifier
- @Resource
使?@Primary注解:當(dāng)存在多個(gè)相同類(lèi)型的Bean注?時(shí),加上@Primary注解,來(lái)確定默認(rèn)的實(shí)現(xiàn)。例如上面代碼:
@Bean @Primary public String name () { return "zhangsan"; }
使?@Qualifier注解:指定當(dāng)前要注?的bean對(duì)象。在@Qualifier的value屬性中,指定注?的bean的名稱(chēng),必須與@Autowired一起用。例如上面代碼:
@Autowired @Qualifier("u1") private User user;
使?@Resource注解:是按照bean的名稱(chēng)進(jìn)?注?。通過(guò)name屬性指定要注?的bean的名稱(chēng)。@Resource是JDK提供的注解。
例如上面代碼:
@Resource(name = "u2") private User user;
@Autowired工作流程
到此這篇關(guān)于SpringIoC與SpringDI的文章就介紹到這了,更多相關(guān)SpringIoC與SpringDI內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
啟動(dòng)springboot應(yīng)用因未配置數(shù)據(jù)庫(kù)報(bào)錯(cuò)的解決方案
這篇文章主要介紹了啟動(dòng)springboot應(yīng)用因未配置數(shù)據(jù)庫(kù)報(bào)錯(cuò)的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11SpringBoot 接口開(kāi)發(fā)教程(httpclient客戶端)
這篇文章主要介紹了SpringBoot 接口開(kāi)發(fā)教程(httpclient客戶端),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03SpringBoot項(xiàng)目啟動(dòng)后自動(dòng)加載系統(tǒng)配置的多種實(shí)現(xiàn)方式
這篇文章主要介紹了SpringBoot項(xiàng)目啟動(dòng)后自動(dòng)加載系統(tǒng)配置的多種實(shí)現(xiàn)方式,并通過(guò)代碼示例講解的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2025-01-01Lombok之@AllArgsConstructor的使用方式
這篇文章主要介紹了Lombok之@AllArgsConstructor的使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-09-09java怎么設(shè)置代理ip實(shí)現(xiàn)高效網(wǎng)絡(luò)請(qǐng)求
無(wú)論是在爬蟲(chóng)、API調(diào)用還是網(wǎng)絡(luò)測(cè)試中,代理IP的使用都變得愈發(fā)重要,本文我們主要來(lái)介紹一下如何在Java中設(shè)置代理IP實(shí)現(xiàn)高效網(wǎng)絡(luò)請(qǐng)求吧2024-11-11LeetCode?動(dòng)態(tài)規(guī)劃之矩陣區(qū)域和詳情
這篇文章主要介紹了LeetCode?動(dòng)態(tài)規(guī)劃之矩陣區(qū)域和詳情,文章基于Java的相關(guān)資料展開(kāi)對(duì)LeetCode?動(dòng)態(tài)規(guī)劃的詳細(xì)介紹,需要的小伙伴可以參考一下2022-04-04利用Java編寫(xiě)一個(gè)出敬業(yè)福的小程序
新年將至,又開(kāi)始掃?;顒?dòng),每年的敬業(yè)福成了大家難過(guò)的坎。所以本文將介紹一個(gè)通過(guò)Java編寫(xiě)的一款福字生成器,感興趣的小伙伴可以試一試2022-01-01