1、@Bean方法注解簡介
上一節(jié)我們介紹了五大類注解,這一節(jié)介紹方法注解@Bean,@Bean作用的對(duì)象是方法,該注解需要搭配五大類注解同時(shí)進(jìn)行使用,因?yàn)轭惙椒ǖ臄?shù)量遠(yuǎn)遠(yuǎn)大于類的數(shù)量,如果使用@Bean注解標(biāo)記方法的類沒有被標(biāo)記,那么Spring Boot項(xiàng)目在啟動(dòng)時(shí)需要遍歷所有的類的所有方法,開銷無疑是巨大的,但如果只遍歷用五大類注解標(biāo)記的類的方法,無疑大大減小了遍歷范圍
2、@Bean注解重命名
@Bean 類注解默認(rèn)情況下,Bean name = 方法名,但是方法名是非常容易重復(fù)的,很可能在兩個(gè)類中有兩個(gè)相同的方法,它們通過方法類注解返回同一個(gè)類的對(duì)象,若這兩個(gè)對(duì)象內(nèi)部屬性不相同,則可能出現(xiàn)誤調(diào)的情況(本想調(diào)用A類的student方法返回名為張三的學(xué)生的,結(jié)果調(diào)用成B類的student方法返回了一個(gè)名為李四的學(xué)生)
并且這種錯(cuò)誤并不會(huì)引起報(bào)錯(cuò),一旦出現(xiàn)錯(cuò)誤非常難以排查問題,而針對(duì)上述問題可以通過給@Bean設(shè)置name屬性獲取對(duì)象
@Controller
public class StudentBeans {
@Bean(name = {"s1", "s2"})
public Student student() {
// 偽代碼構(gòu)建對(duì)象
Student stu = new Student();
stu.setId(1);
stu.setName("張三");
stu.setAge(18);
return stu;
}
}
注:當(dāng)給一個(gè)@Bean設(shè)置了name屬性后,就無法使用方法名獲取Bean對(duì)象了,只能通過設(shè)置的名稱獲取
3、對(duì)象裝配(獲取Bean對(duì)象)
獲取Bean對(duì)象也叫做對(duì)象裝配,就是把注入到Spring Boot中的某個(gè)對(duì)象取出來取出來放到指定類中,這種方法也叫做對(duì)象注入,對(duì)象裝配的方法有三種,接下來將詳細(xì)介紹
3.1 對(duì)象裝配之屬性注入
屬性注入是使用@Autowired 來實(shí)現(xiàn)的,將Service類注入到Controller類中
- 優(yōu)點(diǎn):使用簡單
- 缺點(diǎn):
- 1、功能性問題: 無法注入一個(gè)不可變對(duì)象(final對(duì)象)
- 2、通用型問題:只能適應(yīng)于IoC容器
- 3、設(shè)計(jì)原則問題:更容易違背單一設(shè)計(jì)原則
屬性注入實(shí)例: 將StudentService對(duì)象注入到StudentController類中
package com.demo.controller;
import com.demo.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class StudentController {
@Autowired
private StudentService studentService;
public void sayHi() {
studentService.sayHi();
}
}
package com.demo.service;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;
@Service
public class StudentService {
public void sayHi() {
System.out.println("StudentService sayHi");
}
}
接下來來介紹屬性注入的缺點(diǎn):
1、功能性問題:無法注入final對(duì)象。首先這個(gè)應(yīng)該是jdk的問題而并非spring的問題,final修飾的變量不可被改變,一旦獲取了初始值就不能重新被賦值,如果在類中使用final屬性的成員,要么直接賦值,要么在構(gòu)造函數(shù)中賦值
private StudentService studentService; // 添加final關(guān)鍵字,無法賦值
private final int number; // 報(bào)錯(cuò):必須要直接進(jìn)行賦值,或者寫一個(gè)構(gòu)造函數(shù),在構(gòu)造函數(shù)中賦值
2、通用性問題:只能適用于IoC容器
3、設(shè)計(jì)原則問題:更容易違背單一設(shè)計(jì)原則
3.2 對(duì)象裝配之Set 注入
完全符合單一設(shè)計(jì)原則,每一個(gè)Setter方法只針對(duì)一個(gè)對(duì)象,但是它的缺點(diǎn)也很明顯,不能注入不可變對(duì)象,注入的對(duì)象可以被任意修改
使用方法:
// 2、Setter注入
private StudentService studentService;
@Autowired
public void setStudentService(StudentService studentService) {
this.studentService = studentService;
}
3.3 對(duì)象裝配之構(gòu)造方法注入
Spring Boot 官方推薦的用法,如果當(dāng)前類中只有一個(gè)構(gòu)造方法,那么@Autowired注解可以省略
基本使用方法
// 3、構(gòu)造方法注入
private StudentService studentService;
public StudentController(StudentService studentService) {
this.studentService = studentService;
}
構(gòu)造方法注入的優(yōu)點(diǎn)
1.可注入不可變對(duì)象,不可變對(duì)象可以在構(gòu)造函數(shù)中初始化
2.注入對(duì)象不會(huì)被修改:構(gòu)造方法在對(duì)象創(chuàng)建時(shí)只會(huì)執(zhí)行一次,因此它不存在注入對(duì)象被隨時(shí)(調(diào)用)修改的情況
3.注入對(duì)象會(huì)被完全初始化
4.通用性更好:構(gòu)造方法注入可適用于任何環(huán)境,無論是 IoC 框架還是非 IoC 框架,構(gòu)造方法注入的代碼都是通用的,所以它的通用性更好。
4、@Resource VS @Autowired
功能:兩者都是用來實(shí)現(xiàn)依賴注入的,功能非常相近
@Resource 和 @Autowired 的區(qū)別
- 出身不同:@Autowired來自于Spring,而@Resource來自于JDK的注解
- 使用時(shí)設(shè)置的參數(shù)不同:相比于@Autowired來說,@Resource支持更多的參數(shù)設(shè)置,例如name設(shè)置,根據(jù)名稱獲取Bean
- @Autowired可以用于Setter注入,構(gòu)造函數(shù)注入屬性注入,而@Resource只能用于Setter注入和屬性注入,不能用于構(gòu)造函數(shù)注入
由于@Autowired支持的參數(shù)設(shè)置很少,所以產(chǎn)生了@Qualifier
注解來擴(kuò)充@Autowired 組件的功能
場景:使用@Bean注解向Spring中注入兩個(gè)Student對(duì)象,當(dāng)我們想要從Spring中取出對(duì)象,注入到其他對(duì)象中時(shí),Spring就會(huì)不確定到底使用哪一個(gè)對(duì)象
package com.demo.componect;
import com.demo.model.Student;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Controller;
@Controller
public class StudentBeans {
@Bean(name = {"s1", "s2"})
public Student student1() {
// 偽代碼構(gòu)建對(duì)象
Student stu = new Student();
stu.setId(1);
stu.setName("張三");
stu.setAge(18);
return stu;
}
@Bean
public Student student2() {
Student stu = new Student();
stu.setId(2);
stu.setName("李四");
stu.setAge(20);
return stu;
}
}
@Resource 解決方式:
可以根據(jù)@Bean注解時(shí)設(shè)置的名稱,來確定應(yīng)該調(diào)用哪個(gè)@Bean注解標(biāo)記的方法來獲取指定對(duì)象
@Resource(name = "s1")
private Student student;
@Autowired 解決方式:
-
可以根據(jù) 注入方法名 = 對(duì)象名稱的方式來獲取對(duì)象(不推薦)
@Autowired private Student student2;
-
使用@Qualifier注解可以對(duì)獲取對(duì)象的方法進(jìn)行篩選
@Autowired @Qualifier("student2") private Student student;
5、Bean對(duì)象的作用域
作用域是Bean對(duì)象在整個(gè)Spring框架(項(xiàng)目)中的某種行為模式,Bean對(duì)象默認(rèn)情況下是單例狀態(tài)(singleton),也就是所有人使用的都是同一個(gè)Bean對(duì)象,所以Spring中的Bean的作用域默認(rèn)也是單例模式(singleton)
5.1 驗(yàn)證Bean對(duì)象的默認(rèn)作用域
示例:
@Controller
public class UserController {
@Autowired
private User user1;
public void getUser() {
System.out.println("User1:" + user1);
User u = user1;
u.setName("李四");
System.out.println("u:" + u);
}
}
@Controller
public class UserAdviceController {
@Resource
private User user1;
public void getUser() {
System.out.println("王五 | user1: " + user1);
}
UserController uc = context.getBean("userController", UserController.class);
uc.getUser();
UserAdviceController ua = context.getBean("userAdviceController", UserAdviceController.class);
ua.getUser();
輸出:
User1:User(id=1, name=張三, password=15157722660)
u:User(id=1, name=李四, password=15157722660)
王武 | user1: User(id=1, name=李四, password=15157722660)
可以看到不管是通過創(chuàng)建局部變量,還是重新裝配,獲取到的都是同一個(gè)Bean對(duì)象,這說明Bean對(duì)象全局只有一份,這個(gè)對(duì)象是一個(gè)單例
5.2 Bean對(duì)象的六大作用域詳解
Spring容器在初始化Bean實(shí)例時(shí),同時(shí)會(huì)指定該實(shí)例的作用域,Spring有6種作用域,最后四種是基于Spring MVC生效的:
- 1、singleton : 單例作用域(Spring默認(rèn)選擇該作用域),IOC容器中只存在一個(gè)實(shí)例
- 使用場景:通常無狀態(tài)Bean使用 該作用域,即Bean對(duì)象的屬性狀態(tài)無需更新(對(duì)象不會(huì)被改變)
- 2、prototype : 原型作用域(多例作用域),每次對(duì)該作用域下的Bean的請(qǐng)求都會(huì)創(chuàng)建新的實(shí)例
- 使用場景:通常有狀態(tài)的Bean使用該作用域
- 3、request:請(qǐng)求作用域,每次Http請(qǐng)求會(huì)創(chuàng)建新的Bean實(shí)例,類似于prototype
- 使用場景:一次http的請(qǐng)求和響應(yīng)的共享Bean(限定SpringMVC中使用)
- 4、session:會(huì)話作用域,在一個(gè)http session中,定義一個(gè)Bean實(shí)例
- 使用場景:用戶回話的共享Bean,比如:記錄一個(gè)用戶的登錄信息(限定SpringMVC種使用)
- 5、application:全局作用域,在一個(gè)http servlet Context中,定義一個(gè)Bean實(shí)例。即一個(gè)context對(duì)象使用getBean獲取類得到的是同一個(gè)實(shí)例
- 使用場景:Web應(yīng)用的上下文信息,比如:記錄一個(gè)應(yīng)用的共享信息(限定SpringMVC中使用)
- 6、websocket:在一個(gè)HTTP WebSocket的生命周期中,定義一個(gè)Bean實(shí)例
- 使用場景:WebSocket的每次會(huì)話中,保存一個(gè)Map結(jié)構(gòu)的頭信息,包裹客戶端消息頭。第一次初始化后,直到WebSocket結(jié)束都是用的同一個(gè)Bean(限定Spring WebSocket中使用 )
單例作用域(singleton) vs 全局作用域(application)
- singleton是Spring Core的作用域; application 是Spring Web中的作用域
- singleton作用域IoC容器,而application作用域Servlet容器
5.3 @Scope注解設(shè)置Bean對(duì)象作用域
@Scope標(biāo)簽可以用來聲明Bean的作用域,比如設(shè)置Bean的作用域,如下代碼所示:
@Data
@Controller
public class UserBeans {
// 方法1:
// @Scope("prototype")
// 方法2:
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Bean
public User user1() {
User user = new User();
user.setName("張三");
user.setId(1);
user.setPassword("15157722660");
return user;
}
}
6、Spring執(zhí)行流程
7、Bean生命周期
所謂生命周期指的是一個(gè)對(duì)象從誕生到銷毀的整個(gè)生命過程,我們將這一過程叫做一個(gè)Bean對(duì)象的生命周期
-
實(shí)例化Bean,將字節(jié)碼轉(zhuǎn)換成內(nèi)存中的對(duì)象(即加載,為Bean分配內(nèi)存空間)
-
設(shè)置屬性(Bena注入和裝配)
-
Bean初始化:實(shí)現(xiàn)了各種Aware通知方法,如@BeanPostProcessor(注解初始化前置方法),@PostConstruct xml初始化方法(依賴注入后被執(zhí)行)
-
使用Bean對(duì)象文章來源:http://www.zghlxwxcb.cn/news/detail-860159.html
-
銷毀Bean對(duì)象文章來源地址http://www.zghlxwxcb.cn/news/detail-860159.html
到了這里,關(guān)于【Java Spring】SpringBoot Bean詳解的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!