Spring IoC 容器的使用詳解(最新整理)
1. 應(yīng)用分層
應(yīng)用分層是一種軟件開發(fā)設(shè)計(jì)思想,它將應(yīng)用程序分成 N 個(gè)層次,這 N 個(gè)層次分別負(fù)責(zé)各自的職責(zé),多個(gè)層次之間協(xié)同提供完整的功能,根據(jù)項(xiàng)目的復(fù)雜度,可以分成三層,四層或更多層,MVC 就是把整體的程序分成了 Model(模型), View(視圖), Controller(控制器)三個(gè)層次

由于后端開發(fā),不需要過多的關(guān)注前端,所以又有了一種分層架構(gòu):把整體架構(gòu)分為表現(xiàn)層,業(yè)務(wù)邏輯層,數(shù)據(jù)層,又稱為“三層架構(gòu)”
- 表現(xiàn)層:用來展示數(shù)據(jù)結(jié)果和接收用戶指令,是最接近用戶的一層
- 業(yè)務(wù)邏輯層:負(fù)責(zé)處理業(yè)務(wù)邏輯,包含業(yè)務(wù)邏輯的具體實(shí)現(xiàn)
- 數(shù)據(jù)層:負(fù)責(zé)存儲(chǔ)和管理與應(yīng)用程序相關(guān)的數(shù)據(jù)
在 Spring 的實(shí)現(xiàn)中可以分為下面三個(gè)部分:

Controller:控制層。接收前端發(fā)送的請(qǐng)求,對(duì)請(qǐng)求進(jìn)行處理,并響應(yīng)數(shù)據(jù)
Service:業(yè)務(wù)邏輯層。處理具體的業(yè)務(wù)邏輯
Dao:數(shù)據(jù)訪問層,也被稱為持久層。負(fù)責(zé)數(shù)據(jù)訪問,操作(增刪查改)
2. IoC 的介紹
IoC:也就是控制反轉(zhuǎn)
Spring IoC 是一種設(shè)計(jì)模式,用于解耦對(duì)象之間的依賴關(guān)系,在之前創(chuàng)建的項(xiàng)目中對(duì)象通常會(huì)主動(dòng)創(chuàng)建和管理自己所依賴的對(duì)象,例如,一個(gè)UserService類可能會(huì)在自己的內(nèi)部使用new關(guān)鍵字來創(chuàng)建一個(gè)UserRepository對(duì)象用于數(shù)據(jù)訪問,這樣設(shè)計(jì)看似沒有問題,但是可維護(hù)性卻很低,當(dāng)有很多類創(chuàng)建了各自的對(duì)象時(shí),并且這些對(duì)象之間還有依賴關(guān)系,例如創(chuàng)建 Car ,F(xiàn)ramework,Bottom,Tire 類,從左到右依次存在依賴關(guān)系,當(dāng)其中有一個(gè)類的底層代碼改變之后,調(diào)用鏈上的代碼都需要修改
public class NewCarExample {
public static void main(String[] args) {
Car car = new Car(20);
car.run();
}
//Car類
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);
}
}
}
修改輪胎的構(gòu)造方法之后,底盤也需要修改,當(dāng)修改底盤之后,上層調(diào)用也需要修改
而在 IoC 模式下,對(duì)象的創(chuàng)建和管理這些控制權(quán)被反轉(zhuǎn)了,不再由對(duì)象自身來控制,而是交給外部的容器(IoC 容器)來管理,下面演示一下使用 IoC 的思想來管理對(duì)象
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();
}通過這樣的形式,各個(gè)組件的依賴關(guān)系就發(fā)生了反轉(zhuǎn),統(tǒng)一對(duì)對(duì)象進(jìn)行管理,誰需要這個(gè)對(duì)象直接傳過去一個(gè)對(duì)象,不需要他自己調(diào)用方法進(jìn)行創(chuàng)建

IoC 容器的工作就是把這些對(duì)象進(jìn)行統(tǒng)一管理

通過這種方式進(jìn)行資源的統(tǒng)一管理,在創(chuàng)建實(shí)例時(shí)不需要了解其中的細(xì)節(jié),降低了使用資源雙方的依賴程度
3. IoC 容器的使用
3.1. bean 的存儲(chǔ)
如果想要把一個(gè)對(duì)象交給 IoC 容器來管理,需要在類上添加一個(gè) @Component 注解,此外還有其它的一些注解可以實(shí)現(xiàn):
- 類注解:
@Controller、@Service、@Repository、@Component、@Configuration. - 方法注解:
@Bean.
@Controller
public class UserController {
public void say(){
System.out.println("UserController");
}
}@SpringBootApplication
public class SpringIoCApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(SpringIoCApplication.class, args);
UserController bean = context.getBean(UserController.class);
bean.say();
}
}ApplicationContext 可以理解為 Spring 的上下文,這個(gè)上下文就是指當(dāng)前的運(yùn)行環(huán)境和其他功能,也可以看作是一個(gè)容器,容器中存儲(chǔ)了很多內(nèi)容,這些內(nèi)容是當(dāng)前的運(yùn)行環(huán)境
之后就可以通過拿到的 context 來獲取 bean,關(guān)于獲取 bean 有多種方式:
Object getBean(String var1) throws BeansException; | 根據(jù)bean名稱獲取bean |
T getBean(String var1, Class var2) throws BeansException; | 根據(jù)bean名稱和類型獲取bean |
T getBean(Class var1) throws BeansException; | 根據(jù)類型獲取bean |
Object getBean(String var1, Object... var2) throws BeansException | 按bean名稱和構(gòu)造函數(shù)參數(shù)動(dòng)態(tài)創(chuàng)建bean,只適?于具有原型(prototype)作?域的bean |
T getBean(Class var1, Object... var2) throws BeansException; | 按bean類型和構(gòu)造函數(shù)參數(shù)動(dòng)態(tài)創(chuàng)建bean, 只適?于具有原型(prototype)作?域的 bean |
根據(jù)類型獲取 bean 的話,如果存在多個(gè)相同類型的 bean 那么就不能確定具體要獲取的是哪個(gè) bean ,同理,如果只是根據(jù)名稱來獲取 bean,如果重名的話也是不能正確獲取到 bean 的,所以就有了第三種方式,同時(shí)根據(jù)類型和名稱來獲取 bean
上面這三種獲取 bean 的方式是比較常用的
關(guān)于 IoC 中 bean 的名稱轉(zhuǎn)化規(guī)則:
如果是 UserController 會(huì)被轉(zhuǎn)成 userController,如果是 UController 就還是 UController
UserController bean1 = (UserController) context.getBean("userController");
bean1.say();由于是根據(jù)名稱來獲取 bean,所以獲取到的 bean 不確定是什么類型,會(huì)返回一個(gè) Object 類型,需要強(qiáng)轉(zhuǎn)一下
同時(shí)指定類型和名稱:
UserController bean2 = context.getBean("userController", UserController.class);
bean2.say();
把上面獲取的 bean ,打印一下,發(fā)現(xiàn)獲取的實(shí)例的地址是一樣的,由此可以知道,通過這種方式獲取對(duì)象是基于單例模式實(shí)現(xiàn)的
接下來演示一下 @Service 注解:
@Service
public class UserService {
public void say(){
System.out.println("UserService");
}
}UserService service = context.getBean(UserService.class); service.say();
然后發(fā)現(xiàn)和上面一樣也是可以運(yùn)行的,其它的幾個(gè)類注解也是一樣的
那么為什么實(shí)現(xiàn)的功能一樣,還需要分這么多不同的注解,這個(gè)是和之前的應(yīng)用分層是對(duì)應(yīng)的,通過不同的類注解來了解當(dāng)前類的用途
@Controller:控制層,接受請(qǐng)求,對(duì)請(qǐng)求進(jìn)行處理,并進(jìn)行響應(yīng)
@Servie:業(yè)務(wù)邏輯層,處理具體的業(yè)務(wù)邏輯
@Repository:數(shù)據(jù)訪問層, 也稱為持久層,負(fù)責(zé)數(shù)據(jù)訪問操作
@Configuration:配置層,處理項(xiàng)目中的一些配置信息
3.2. 方法注解@Bean
上面四個(gè)注解都是 @Component的衍生注解
類注解是添加到某個(gè)類上的,但是存在兩個(gè)問題:
- 如果使用外部包里的類,沒辦法添加注解
- 同時(shí),由于類注解默認(rèn)創(chuàng)建的對(duì)象是單例對(duì)象,如果需要多個(gè)對(duì)象就需要調(diào)整
方法注解 @Bean就可以解決上述問題
例如,在一個(gè)外部包里有一個(gè) UserInfo 類
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserInfo {
private String name;
private Integer age;
}可以通過在獲取對(duì)象的方法上加上@Bean
@Component
public class UserInfoComponent {
@Bean
public UserInfo userInfo(){
return new UserInfo("zhangsan",20);
}
}
如果說需要?jiǎng)?chuàng)建對(duì)個(gè)對(duì)象的話:

這個(gè)問題就是在獲取 bean 的時(shí)候發(fā)現(xiàn)了具有相同類型的 bean,可以直接通過獲取 bean 的名稱,這里的名稱和方法注解下方法名是對(duì)應(yīng)的
UserInfo bean = (UserInfo) context.getBean("userInfo");
System.out.println(bean);上面的注解,無論是類注解還是方法注解,都可以實(shí)現(xiàn)重命名

重命名之后就需要使用改之后的名字了
4. 掃描路徑


如果說把啟動(dòng)類放到其他目錄下再運(yùn)行就會(huì)報(bào)錯(cuò),是因?yàn)樯厦娼榻B的注解如果想要生效,是需要配置掃描路徑的,默認(rèn)的掃描范圍是 Spring Boot 啟動(dòng)類所在的包和它的子包,可以通過@ComponentScan來配置掃描路徑

在 @ComponentScan源碼中,是支持傳入一個(gè)數(shù)組的,如果想要配置多個(gè)掃描路徑可以直接傳入一個(gè)數(shù)組

@ComponentScan({"com.example.service","com.example.controller"})
到此這篇關(guān)于SpringIoC 容器的使用的文章就介紹到這了,更多相關(guān)SpringIoC 容器使用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringSecurity rememberme功能實(shí)現(xiàn)過程解析
這篇文章主要介紹了SpringSecurity rememberme功能實(shí)現(xiàn)過程解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03
SpringBoot集成ENC對(duì)配置文件進(jìn)行加密的流程步驟
Spring Boot Encoder,即Spring Boot加密模塊,它提供了一種簡單的方式來集成安全編碼功能到Spring Boot應(yīng)用程序中,它是Spring Security框架的一部分,旨在幫助開發(fā)者輕松地處理數(shù)據(jù)加密,本文給大家介紹了SpringBoot集成ENC對(duì)配置文件進(jìn)行加密的流程步驟2024-12-12
如何將字符串、字節(jié)數(shù)組轉(zhuǎn)為輸入流
這篇文章主要介紹了如何將字符串、字節(jié)數(shù)組轉(zhuǎn)為輸入流問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-05-05
Java用BigDecimal類解決Double類型精度丟失的問題
這篇文章主要介紹了Java用BigDecimal類解決Double類型精度丟失的問題,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下2020-12-12
Spring Boot 中PageHelper 插件使用配置思路詳解
這篇文章主要介紹了Spring Boot 中PageHelper 插件使用配置及實(shí)現(xiàn)思路,通過引入myabtis和pagehelper依賴,在yml中配置mybatis掃描和實(shí)體類,具體實(shí)現(xiàn)方法跟隨小編一起看看吧2021-08-08
IDEA實(shí)現(xiàn)遠(yuǎn)程調(diào)試步驟詳解
這篇文章主要介紹了IDEA實(shí)現(xiàn)遠(yuǎn)程調(diào)試步驟詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09
Jmeter內(nèi)置變量vars和props的使用詳解
JMeter是一個(gè)功能強(qiáng)大的負(fù)載測試工具,它提供了許多有用的內(nèi)置變量來支持測試過程,其中最常用的變量是 vars 和 props,本文通過代碼示例詳細(xì)給大家介紹了Jmeter內(nèi)置變量vars和props的使用,需要的朋友可以參考下2024-08-08

