淺析Spring?中?Bean?的理解與使用
大白話講解:
從廣義上 Spring 注解可以分為兩類:
- 一類注解是用于注冊 Bean
假如 IoC 容器是一間空屋子,首先這間空屋子啥都沒有,我們要吃大餐,我們就要從外部搬運食材和餐具進來。這里把某一樣食材或者某一樣餐具搬進空屋子的操作就相當于每個注冊 Bean 的注解作用類似。注冊 Bean 的注解作用就是往 IoC容器中放(注冊)東西!
用于注冊 Bean 的注解:比如 @Component、@Repository、@Controller、@Service、@Configuration 這些注解就是用于注冊 Bean,放進 IoC 容器中,一來交給 Spring 管理方便解耦,二來還可以進行二次使用,啥是二次使用呢?這里的二次使用可以理解為:在你開始從外部搬運食材和餐具進空屋子的時候,一次性搬運了豬肉、羊肉、鐵勺、筷子四樣東西,這個時候你要開始吃大餐,首先你吃東西的時候肯定要用筷子或者鐵勺,別說你手抓,只要你需要,你就會去找,這個時候發(fā)現(xiàn)你已經把筷子或者鐵勺放進了屋子,你就不同再去外部拿筷子進屋子了,意思就是 IoC 容器中已經存在,就可以直接拿去用,而不必再去注冊!而拿屋子里已有的東西的操作就是下面要講的關于使用 Bean 的注解!
- 一類注解是用于使用 Bean
用于使用 Bean 的注解:比如 @Autowired、@Resource 注解,這些注解就是把屋子里的東西自己拿來用,如果你要拿,前提一定是屋子(IoC)里有的,不然就會報錯。比如你要做一道牛肉拼盤需要五頭牛做原材料才行,你現(xiàn)在鍋里只有四頭牛,這個時候你知道,自己往屋子里搬過五頭牛,這個時候就直接把屋子里的那頭牛直接放進鍋里,完成牛肉拼盤的組裝。是的這些注解就是需要啥,只要容器中有就往容器中拿,就是這么豪橫!而這些注解又有各自的區(qū)別,比如 @Autowired 用在筷子上,這筷子你可能想用木質的,或許只想用鐵質的,@Autowired 作用在什么屬性的筷子就那什么筷子,而 @Resource 如果用在安格斯牛肉上面,就指定要名字就是安格斯牛肉的牛肉。
一、定義
Bean 是 Spring 框架中最核心的兩個概念之一(另一個是面向切面編程 AOP)
Spring 官方文檔對 bean 的解釋是:
In Spring, the objects that form the backbone of your application and that are managed by the Spring IoC container are called beans. A bean is an object that is instantiated, assembled, and otherwise managed by a Spring IoC container.
翻譯過來就是:
在 Spring 中,構成應用程序主干并由 Spring IoC 容器管理的對象稱為 bean。bean 是由Spring IoC 容器實例化、組裝和管理的對象。
從上面翻譯過來意思來看:
- bean 是對象,一個或者多個不限定
- bean 由 Spring 中一個叫 IoC 的東西管理的
- 我們的應用程序由一個個 bean 構成
那么問題來了,IoC 是什么呢?
二、控制反轉(IoC)
控制反轉英文全稱:Inversion of Control,簡稱就是 IoC。控制反轉通過依賴注入(DI)方式實現(xiàn)對象之間的松耦合關系。程序運行時,依賴對象由輔助程序動態(tài)生成并注入到被依賴對象中,動態(tài)綁定兩者的使用關系。Spring IoC 容器就是這樣的輔助程序,它負責對象的生成和依賴的注入,然后再交由我們使用。
1、什么是依賴注入與控制反轉呢?先通過一個例子來理解一下
首先有一個類叫做 Student,里面有兩個成員變量分別是 id 和 name,并提供 set、get方法。
public class Student { private Integer id; private String name; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
另外一個類叫做 StudentManager
public class StudentManager { private Student student; public void setStudent(Student student) { this.student = student; } public void show() { System.out.println(student.getId()); System.out.println(student.getName()); } }
這個 StudentManager 類有個成員是 Student 的一個對象,然后它的 show 方法能夠打印這個 student 的 id 以及 name,并提供了 setStudent 方法初始化 Student 對象。我們可以說,StudentManager(被依賴對象) 是依賴于 Student(依賴對象)的。
有一個問題,StudentManager 與 Student 之間的耦合非常緊密,假如我們還沒有來的及對 StudentManager 的 student 綁定對象,卻調用了 show 方法的話,那么程序將會拋出空指針異常。所以 Spring 提供了一套叫做控制反轉與依賴注入這套機制,目的就是為了解耦。
在 Spring 中,你不需要自己創(chuàng)建對象,你只需要告訴 Spring,哪些類我需要創(chuàng)建出對象,然后在啟動項目的時候 Spring 就會自動幫你創(chuàng)建出該對象,并且只存在一個類的實例。這個類的實例在 Spring 中被稱為 Bean。而這種模式,我們稱之為“單例模式”。也就是一個類只有一個實例的意思。
那么 Spring 是靠什么來了解究竟哪些類需要幫我們創(chuàng)建呢,這里介紹最常用的兩種方式------Java 注解配置,Java 代碼配置。之前還有 XML 配置等,但是之前的現(xiàn)在已經不推薦使用了。
首先介紹的是 Java 注解配置,這是最簡單也是最常用的一種方式。
聲明 | 含義 |
@Component | 當前類是組件,沒有明確的意思 |
@Service | 當前類在業(yè)務邏輯層使用 |
@Repository | 當前類在數(shù)據(jù)訪問層 |
@Controller | 當前類在展示層(MVC)使用 |
以上四種聲明方式效果完全一致,使用不同的關鍵詞是為了給閱讀的人能夠快速了解該類屬于哪一層。
使用方法為:在定義的實體類前使用該注解。讓我們看下面一段代碼
@Component public class Student { private Integer id; private String name; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
我們在剛才的 Student 類前面,加上了 @Component 注解,成功告訴 Spring:你要在項目創(chuàng)建運行時幫我創(chuàng)建 Student 類的 Bean (對象)。
好了,這時候添加“依賴”就已經做完了,但是還沒完,我們雖然讓 Spring 幫我們創(chuàng)建了對象,但是 StudentManager 怎么知道這個對象在哪呢?所以接下來,我們要告訴 StudentManager 剛才 Spring 幫我們創(chuàng)建的 Bean (對象)到底在哪,也就是使用(“注入”)這個 Bean。
我們來看看注入注解的語法:
聲明 | 含義 |
@Autowired | 根據(jù) Bean 的 Class 類型來自動裝配 |
@Inject | 翻譯為“注入”最易懂得注入注解 |
@Resource | 翻譯為“資源”,根據(jù) Bean 得屬性名稱(id 或 name)自動裝配 |
使用方法:在我們需要注入依賴的成員變量前使用該注解,看一下下面一段代碼
@Component public class StudentManager { @Autowired private Student student; public void show() { System.out.println(student.getId()); System.out.println(student.getName()); } }
可以看到,在聲明成員變量 Student 的前面我們使用了 @Autowired,所以 Spring 會自動幫我們使用(注入)一個 Bean,我們就再也不用擔心忘記綁定對象而出現(xiàn)空指針了。但是可以發(fā)現(xiàn),雖然我們告訴了 Spring 哪些類是需要添加依賴,哪些類是需要注入 Bean,但是 Spring 還需要我們做一次配置,來真正完成這樣一個操作。
2、讓 Spring 控制類構建過程
不用 new,讓 Spring 控制 new 過程。在 Spring 中,我們基本不需要 new 一個類,這些都是讓 Spring 去做的。Spring 啟動時會把所需的類實例化對象,如果需要依賴,則先實例化依賴,然后實例化當前類。因為依賴必須通過構建函數(shù)傳入,所以實例化時,當前類就會接收并保存所有依賴的對象。這一步也就是所謂的依賴注入。
3、這就是 IOC
在 Spring 中,類的實例化、依賴的實例化、依賴的傳入都交由 Spring Bean 容器控制,而不是用 new 方式實例化對象、通過非構造函數(shù)方法傳入依賴等常規(guī)方式。實質的控制權已經交由程序管理,而不是程序員管理,所以叫控制反轉。
三、 @Bean 注解的使用
1、使用說明
- @Bean 注解作用在方法上,產生一個 Bean 對象,然后這個 Bean 對象交給 Spring 管理,剩下的你就不用管了。產生這個 Bean 對象的方法 Spring 只會調用一次,隨后這個 Spring 將會將這個 Bean 對象放在自己的 IOC 容器中。
- @Bean 方法名與返回類名一致,首字母小寫。
- @Component、@Repository、@Controller、@Service 這些注解只局限于自己編寫的類,而 @Bean 注解能把第三方庫中的類實例加入 IOC 容器中并交給 Spring 管理。
- @Bean 一般和 @Component 或者 @Configuration 一起使用
2、Bean 名稱
2.1、默認情況下 Bean 名稱就是方法名(首字母小寫),比如下面 Bean 名稱便是 myBean
@Bean public MyBean myBean() { return new MyBean(); }
2.2、@Bean 注解支持設置別名。比如下面除了主名稱 myBean 外,還有個別名 myBean1(兩個都可以使用)
@Bean("myBean1") public MyBean myBean() { return new MyBean(); }
2.3、@Bean 注解可以接受一個 String 數(shù)組設置多個別名。
比如下面除了主名稱 myBean 外,還有別名 myBean1、myBean2(三個都可以使用)
@Bean({"myBean1","myBean2"}) public MyBean myBean() { return new MyBean(); }
3、@Bean 與其他注解產生的火花
@Bean 注解常常與 @Scope、@Lazy、@DependsOn 和 @link Primary 注解一起使用
3.1、@Profile 注解
為在不同環(huán)境下使用不同的配置提供了支持,如開發(fā)環(huán)境和生產環(huán)境的數(shù)據(jù)庫配置是不同的
@Bean @Profile("!dev") // 不是dev環(huán)境的能使用這個bean public MyBean myBean() { MyBean myBean = new MyBean(); myBean.setPort("8080"); return myBean; }
3.2、@Scope 注解
在 Spring 中對于 bean 的默認處理都是單例的,我們通過上下文容器.getBean方法拿到 bean 容器,并對其進行實例化,這個實例化的過程其實只進行一次,即多次 getBean 獲取的對象都是同一個對象,也就相當于這個 bean 的實例在 IOC 容器中是 public 的,對于所有的 bean 請求來講都可以共享此 bean。@Scope 注解將其改成 prototype 原型模式(每次獲取 Bean 的時候會有一個新的實例)
@Bean @Scope("prototype") public MyBean myBean() { MyBean myBean = new MyBean(); myBean.setPort("8080"); return myBean; }
@SpringBootApplication @MapperScan("com.example.quartzdemo.dao")//使用MapperScan批量掃描所有的Mapper接口; public class QuartzDemoApplication { public static void main(String[] args) { ConfigurableApplicationContext context = SpringApplication.run(QuartzDemoApplication.class, args); MyBean myBean = (MyBean) context.getBean("myBean"); System.out.println(myBean); MyBean myBean2 = (MyBean) context.getBean("myBean"); System.out.println(myBean2); } }
打印輸出結果:
com.example.quartzdemo.config.MyBean@49601f82
com.example.quartzdemo.config.MyBean@23e44287
將 @Scope("prototype") 刪除掉,再運行啟動類,打印結果如下:
com.example.quartzdemo.config.MyBean@4cdd2c73
com.example.quartzdemo.config.MyBean@4cdd2c73
3.3、@Lazy 注解:
在 Spring 框架中,默認會在啟動時會創(chuàng)建所有的 Bean 對象,但有些 bean 對象假如長時間不用,啟動時就創(chuàng)建對象,會占用其內存資源,從而造成一定的資源浪費,此時我們可以基于懶加載策略延遲對象的創(chuàng)建。
@Bean @Lazy public MyBean myBean() { MyBean myBean = new MyBean(); myBean.setPort("8080"); return myBean; }
3.4、@DependsOn注解:
表示在當前 Bean 創(chuàng)建之前需要先創(chuàng)建特定的其他 Bean
四、Bean 規(guī)范
- 所有屬性為 private
- 提供默認構造方法
- 提供 getter 和 setter
- 實現(xiàn) Serializable (比如可以實現(xiàn)Serializable 接口,用于實現(xiàn)bean的持久性)
- 屬性類型使用包裝類
五、參考文檔
到此這篇關于Spring 中 Bean 的理解與使用的文章就介紹到這了,更多相關Spring Bean 使用內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
spring boot如何使用AOP統(tǒng)一處理web請求
這篇文章主要介紹了spring boot如何使用AOP統(tǒng)一處理web請求,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2019-12-12java開發(fā)Dubbo注解Adaptive實現(xiàn)原理
這篇文章主要為大家介紹了java開發(fā)Dubbo注解Adaptive實現(xiàn)原理詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-09-09從0到1學SpringCloud之SpringCloud?gateway網關路由配置示例詳解
Spring?Cloud?Gateway的目標提供統(tǒng)一的路由方式且基于Filter?鏈的方式提供了網關基本的功能,?例如:安全、監(jiān)控、指標和限流?,這篇文章主要介紹了從0到1學SpringCloud之SpringCloud?gateway網關路由配置示例詳解,需要的朋友可以參考下2023-04-04Java StringUtils字符串分割轉數(shù)組的實現(xiàn)
這篇文章主要介紹了Java StringUtils字符串分割轉數(shù)組的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-09-09Spring Cloud Ribbon實現(xiàn)客戶端負載均衡的示例
本篇文章主要介紹了Spring Cloud Ribbon實現(xiàn)客戶端負載均衡的示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-02-02mybatis項目實現(xiàn)動態(tài)表名的三種方法
有時在開發(fā)過程中java代碼中的表名和數(shù)據(jù)庫的表名并不是一致的,此時我們就需要動態(tài)的設置表名,本文主要介紹了mybatis項目實現(xiàn)動態(tài)表名的三種方法,具有一定的參考價值,感興趣的可以了解一下2024-01-01關于Java中的實體類要?implements?Serializable的原因分析
這篇文章主要介紹了Java中的實體類為什么要?implements?Serializable,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-06-06jackson使用@JsonSerialize格式化BigDecimal解決.00不顯示問題
這篇文章主要介紹了jackson使用@JsonSerialize格式化BigDecimal解決.00不顯示問題,本文直接給出實現(xiàn)代碼,需要的朋友可以參考下2015-02-02