首頁 > 軟體

淺析Spring 中 Bean 的理解與使用

2023-03-19 06:01:22

大白話講解:

從廣義上 Spring 註解可以分為兩類:

  • 一類註解是用於註冊 Bean

假如 IoC 容器是一間空屋子,首先這間空屋子啥都沒有,我們要吃大餐,我們就要從外部搬運食材和餐具進來。這裡把某一樣食材或者某一樣餐具搬進空屋子的操作就相當於每個註冊 Bean 的註解作用類似。註冊 Bean 的註解作用就是往 IoC容器中放(註冊)東西!

用於註冊 Bean 的註解:比如 @Component、@Repository、@Controller、@Service、@Configuration 這些註解就是用於註冊 Bean,放進 IoC 容器中,一來交給 Spring 管理方便解耦,二來還可以進行二次使用,啥是二次使用呢?這裡的二次使用可以理解為:在你開始從外部搬運食材和餐具進空屋子的時候,一次性搬運了豬肉、羊肉、鐵勺、筷子四樣東西,這個時候你要開始吃大餐,首先你吃東西的時候肯定要用筷子或者鐵勺,別說你手抓,只要你需要,你就會去找,這個時候發現你已經把筷子或者鐵勺放進了屋子,你就不同再去外部拿筷子進屋子了,意思就是 IoC 容器中已經存在,就可以直接拿去用,而不必再去註冊!而拿屋子裡已有的東西的操作就是下面要講的關於使用 Bean 的註解!

  • 一類註解是用於使用 Bean

用於使用 Bean 的註解:比如 @Autowired、@Resource 註解,這些註解就是把屋子裡的東西自己拿來用,如果你要拿,前提一定是屋子(IoC)裡有的,不然就會報錯。比如你要做一道牛肉拼盤需要五頭牛做原材料才行,你現在鍋裡只有四頭牛,這個時候你知道,自己往屋子裡搬過五頭牛,這個時候就直接把屋子裡的那頭牛直接放進鍋裡,完成牛肉拼盤的組裝。是的這些註解就是需要啥,只要容器中有就往容器中拿,就是這麼豪橫!而這些註解又有各自的區別,比如 @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)方式實現物件之間的鬆耦合關係。程式執行時,依賴物件由輔助程式動態生成並注入到被依賴物件中,動態繫結兩者的使用關係。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 中,你不需要自己建立物件,你只需要告訴 Spring,哪些類我需要建立出物件,然後在啟動專案的時候 Spring 就會自動幫你建立出該物件,並且只存在一個類的範例。這個類的範例在 Spring 中被稱為 Bean。而這種模式,我們稱之為“單例模式”。也就是一個類只有一個範例的意思。

        那麼 Spring 是靠什麼來了解究竟哪些類需要幫我們建立呢,這裡介紹最常用的兩種方式------Java 註解設定,Java 程式碼設定。之前還有 XML 設定等,但是之前的現在已經不推薦使用了。

        首先介紹的是 Java 註解設定,這是最簡單也是最常用的一種方式。

宣告含義
@Component當前類是元件,沒有明確的意思
@Service當前類在業務邏輯層使用
@Repository當前類在資料存取層
@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:你要在專案建立執行時幫我建立 Student 類的 Bean (物件)

        好了,這時候新增“依賴”就已經做完了,但是還沒完,我們雖然讓 Spring 幫我們建立了物件,但是 StudentManager 怎麼知道這個物件在哪呢?所以接下來,我們要告訴 StudentManager 剛才 Spring 幫我們建立的 Bean (物件)到底在哪,也就是使用(“注入”)這個 Bean。

        我們來看看注入註解的語法:

宣告含義
@Autowired根據 Bean 的 Class 型別來自動裝配
@Inject翻譯為“注入”最易懂得注入註解
@Resource翻譯為“資源”,根據 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,我們就再也不用擔心忘記繫結物件而出現空指標了。但是可以發現,雖然我們告訴了 Spring 哪些類是需要新增依賴,哪些類是需要注入 Bean,但是 Spring 還需要我們做一次設定,來真正完成這樣一個操作。

2、讓 Spring 控制類構建過程

不用 new,讓 Spring 控制 new 過程。在 Spring 中,我們基本不需要 new 一個類,這些都是讓 Spring 去做的。Spring 啟動時會把所需的類範例化物件,如果需要依賴,則先範例化依賴,然後範例化當前類。因為依賴必須通過構建函數傳入,所以範例化時,當前類就會接收並儲存所有依賴的物件。這一步也就是所謂的依賴注入。

3、這就是 IOC

在 Spring 中,類的範例化、依賴的範例化、依賴的傳入都交由 Spring Bean 容器控制,而不是用 new 方式範例化物件、通過非建構函式方法傳入依賴等常規方式。實質的控制權已經交由程式管理,而不是程式設計師管理,所以叫控制反轉。

三、 @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 陣列設定多個別名。

比如下面除了主名稱 myBean 外,還有別名 myBean1、myBean2(三個都可以使用)

@Bean({"myBean1","myBean2"})
public MyBean myBean() {
    return new MyBean();
}

3、@Bean 與其他註解產生的火花

@Bean 註解常常與 @Scope、@Lazy、@DependsOn 和 @link Primary 註解一起使用

3.1、@Profile 註解

為在不同環境下使用不同的設定提供了支援,如開發環境和生產環境的資料庫設定是不同的

@Bean
@Profile("!dev")  // 不是dev環境的能使用這個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 框架中,預設會在啟動時會建立所有的 Bean 物件,但有些 bean 物件假如長時間不用,啟動時就建立物件,會佔用其記憶體資源,從而造成一定的資源浪費,此時我們可以基於懶載入策略延遲物件的建立。

    @Bean
    @Lazy
    public MyBean myBean() {
        MyBean myBean = new MyBean();
        myBean.setPort("8080");
        return myBean;
    }

3.4、@DependsOn註解:

表示在當前 Bean 建立之前需要先建立特定的其他 Bean

四、Bean 規範

  • 所有屬性為 private
  • 提供預設構造方法
  • 提供 getter 和 setter
  • 實現 Serializable (比如可以實現Serializable 介面,用於實現bean的永續性)
  • 屬性型別使用包裝類

五、參考檔案

spring bean是什麼

大白話講解Spring的@bean註解

Java bean 是個什麼概念?

到此這篇關於Spring 中 Bean 的理解與使用的文章就介紹到這了,更多相關Spring Bean 使用內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com