用IOC和DI解決懶人老闆想喝咖啡但不想自己動手的窘迫

我可是千機傘發表於2020-09-23

        懶人公司裡,所有人都非常的懶惰,唯獨他們的程式設計師還在拼命的奮鬥。這一天老闆想喝咖啡。

public void boss(){
        System.out.println("我想喝咖啡");
    }

這時候老闆走到了咖啡機。

 public void cofo_jiqi(){
        System.out.println("我是咖啡機");
    }

但是老闆現在並不會泡咖啡(別問我為什麼不會,這是個蠢貨懶老闆)這時我教給他了泡咖啡的方法。

 public void boss(){
        System.out.println("我想喝咖啡");
        //泡咖啡的方法
        Cofo cofo = new Cofo();
        cofo.cofo_jiqi();

    }

好啦,老闆成功的泡好了一杯咖啡。但是沒過多久,他又想喝咖啡了。由於這是懶人公司,所以老闆也懶到極致,他叫了自己手下唯一勤奮的程式設計師:小王,去讓他幫自己泡咖啡。

        chengxuyuan Mr_wang = new chengxuyuan();

程式設計師小王叫過來了

sequenceDiagram
老闆 ->> 小王: 小王啊,我們公司對你怎麼樣?
小王 ->>老闆:對我可是相當好!
老闆->>小王: 那好,我有個重要的事情交給你
小王 ->>老闆:什麼事情!難道是拿年終獎了!
老闆->>小王: 咳咳,幫我泡杯咖啡,什麼都有。

小王被老闆忽悠的團團轉,屁顛屁顛的要幫老闆泡咖啡。

chengxuyuan Mr_wang = new chengxuyuan();
 Mr_wang.pao_cofo();

可是小王的技能只有寫程式碼,我們還要教會他泡咖啡才行。

class chengxuyuan{
    public void Write(){
        System.out.println("我會寫程式碼");
    }
    public void pao_cofo(){
        System.out.println("我會泡咖啡");
        Cofo cofo = new Cofo();
        cofo.cofo_jiqi();
    }
}

好了,現在我們教會小王泡咖啡了。這時候這個懶人老闆的得力泡咖啡助手就是小王了。
        講了這麼多,其實經過這個例子,我們就已經明白了iOC中控制反轉的概念了。之前老闆想泡咖啡,必須自己來。
在這裡插入圖片描述
老闆需要知道,這個咖啡機是怎麼用的,但是老闆怎麼會連這種低階的東西都學呢!!!所以這時候我們就需要用一個工具人。
在這裡插入圖片描述
這時候老闆只需要張張嘴,讓小王這個工具人來做,自己不需要動,也不需要知道咖啡機怎麼用,都讓小王來幫你,你需要做的就是躺在座椅上,飯來張口。我們做老闆可能都喜歡這種瀟灑的生活,程式也不例外。

1.控制反轉

        光聽名字,大家應該就可以聽出個一二,控制反轉嘛,不就是本來屬於自己控制的事情,反過來讓其他人幫你控制。其實話粗理不粗,控制反轉的意思大可這樣理解。即通過控制傳遞給我程式碼,但實際上我並沒有直接依賴於它們。這並非我的程式碼呼叫了什麼更通用的程式碼,而是框架允許我插入自定義的行為。像這樣系統設計使用的就是所謂的控制反轉(簡稱IOC)。
        當然肯定有人會想,這和我平常new物件有什麼區別什麼好處呢。當然我上面說的例子中,這些好處肯定不足以什麼,那我就仔細講講他的好處。

通過解耦改善模組化

        在我們的實際開發過程中,都是一塊大肉多人分,一個專案多人做,這樣的就是模組化。要是我們平常用new來獲取物件,就大大增加了程式碼的耦合。讓兩個類變的互不可缺,這個是很不應該的。假如後期的專案,各類開始分層,挪動,到時候要是還是用new的話,必定會出現很多錯誤。這樣的話,程式設計師不可能一個個找錯誤,那太麻煩了。所以這時候我們的IOC容器就會幫我們自動找類,來給你注入這個物件。

不用考慮建立物件的過程,只需要找IOC容器拿物件

        我們可以把我們的小王當做一個ioc容器。我們只需要考慮的是,喝什麼咖啡,而不用考慮這個咖啡是怎麼泡出來的。大大的節省了程式碼量和邏輯。

        那我們該如何建立一個IOC容器,讓他幫我們管理物件呢。我們用Spring來做這個例子。

配置bean

        就是將我們的物件資訊,放入bean裡面

<bean id = "chengxuyuan" class="com.sun.been.chengxuyuan">
    </bean>
    <bean id ="cofo" class="com.sun.been.Cofo">

    </bean>

ID放的是我們的物件名字,類似A a = new A();裡的 a。
class就是這個類的地址。
那麼這兩個物件有了,我們想想,還需要寫什麼嗎??對了,我們既然要讓小王幫我們泡咖啡,那我們必須要學會怎麼跑咖啡啊,這時候我們給他寫個泡咖啡的方法。

    <bean id = "chengxuyuan" class="com.sun.been.chengxuyuan">
        <property name="cofo" ref="cofo"></property>
    </bean>
    <bean id ="cofo" class="com.sun.been.Cofo">

    </bean>

好了,那我們來看看,程式碼該如何獲取到這些配置的呢。
首先我們要從IOC容器中獲得程式設計師小王這個物件。

 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
        chengxuyuan Mr_wang = applicationContext.getBean("chengxuyuan",chengxuyuan.class);

        這個是個固定用法,意思是獲得一個容器物件ApplicationContext,讓他讀取bean.xml裡的配置。接下來,我們用容器的物件,將程式設計師小王給獲取出來,之所以要用chengxuyuan.class,是因為傳過去的應該是個位元組碼物件。需要進行型別轉換。當然也可以在前面寫個強轉,兩者都可實現。
        這時候小王叫來了,要讓小王泡咖啡了,但是小王這時候還是不會的,我們需要教他,這時候我們就需要用DI來注入這個方法。

 public void setCofo(Cofo cofo) {
        this.cofo = cofo;
    }

    private Cofo cofo;

        這時,你肯定就有疑惑了,這樣寫不會空指標異常嗎,因為我們沒有給物件給到cofo啊。no!no!no!這裡我們的IOC容器就已經從bean.xml,找到了這個物件,並且讓這個物件,等於bean.xml裡我們已經配置好了的 ,cofo這個物件。所以這就是IOC容器一個強大之處,di注入。(這裡是個固定用法,想讓Ioc幫你注入,就只能這樣寫)這時候我們來執行一下在這裡插入圖片描述
這個咖啡就已經泡好了。
        那大家有沒有發現,一直用bean.xml來配置物件資訊,是不是太麻煩了。要是有一百個物件,那對於以後的運維都是一個困難,那我們有沒有一個好的方法來簡化xml呢。答案是有的,Spring專門為我們準備了註解,來完成bean.xml裡的操作。我們需要使用以下幾個註解:

@Autowired顧名思義,就是自動裝配,其作用是為了消除程式碼Java程式碼裡面的getter/setter與bean屬性中的property。當然,getter看個人需求,如果私有屬性需要對外提供的話,應當予以保留。

@Autowired預設按型別匹配的方式,在容器查詢匹配的Bean,當有且僅有一個匹配的Bean時,Spring將其注入@Autowired標註的變數中。

用法就是寫在你的方法的上面。

//    public void setCofo(Cofo cofo) {
//        this.cofo = cofo;
//    }
    @Autowired
    private Cofo cofo;

這個註解在XML中將

        <property name="cofo" ref="cofo"/>

替代了。

省去了set方法,可以直接匹配方法。但是要注意,要是這個bean,有多個,再或者是沒有,都會報錯,因為是按型別匹配這個物件。那一旦出現了這種情況該怎麼辦呢?

@Resource註解與@Autowired註解作用非常相似
這是詳細一些的用法,說一下@Resource的裝配順序:
(1)、@Resource後面沒有任何內容,預設通過name屬性去匹配bean,找不到再按type去匹配
(2)、指定了name或者type則根據指定的型別去匹配bean
(3)、指定了name和type則根據指定的name和type去匹配bean,任何一個不匹配都將報錯

然後,區分一下@Autowired和@Resource兩個註解的區別:
(1)、@Autowired預設按照byType方式進行bean匹配,@Resource預設按照byName方式進行bean匹配
(2)、@Autowired是Spring的註解,@Resource是J2EE的註解,這個看一下匯入註解的時候這兩個註解的包名就一清二楚了
Spring屬於第三方的,J2EE是Java自己的東西,因此,建議使用@Resource註解,以減少程式碼和Spring之間的耦合。

@Service
@Repository
@Component
這三個的用法是一樣的,只不過是由於分層,才會出現三個註解,其底層程式碼都是一樣的。將這個類放入IOC容器中,替代了xml中

 <bean id = "chengxuyuan" class="com.sun.been.chengxuyuan">

    </bean>
    <bean id ="cofo" class="com.sun.been.Cofo">

    </bean>

的操作。但是這樣建立的bean都是單例模式,假如我們的實際開發中要用到多例模式,配置@Scope即可,預設是”singleton”即單例,”prototype”表示原型即每次都會new一個新的出來。
但是要注意通過註解來配置ioc容器,我們的bean.xml是需要新增一行標籤,以此來掃描我們的註解。

    <context:component-scan base-package="com.sun"></context:component-scan>

裡面的引數package就是填寫需要掃描註解的包。

那麼現在 我們的整體程式碼就已經變成這樣了

package com.sun.been;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Component;

/**
 * SpringIoc和DI例子
 */
public class test {
    /**
     * 測試類入口
     */
    public static void main(String[] args) {
        /**
         * 掃描配置檔案,獲取物件
         */
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
        chengxuyuan Mr_wang = applicationContext.getBean("chengxuyuan",chengxuyuan.class);
        System.out.println("我想喝咖啡");

        Mr_wang.pao_cofo();
    }
    }

/**
 * 程式設計師類
 */
@Component
class chengxuyuan{

    @Autowired()
    private Cofo cofo;
    public void Write(){
        System.out.println("我會寫程式碼");
    }
    public void pao_cofo(){
        System.out.println("我會泡咖啡");
        cofo.cofo_jiqi();
    }
}

/**
 * 咖啡類
 */
@Component
class Cofo{
    public void cofo_jiqi(){
        System.out.println("我是咖啡機");
    }
        }

bean.xml也只剩

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
             http://www.springframework.org/schema/beans/spring-beans.xsd
         http://www.springframework.org/schema/context
         http://www.springframework.org/schema/context/spring-context-4.0.xsd">
    <context:component-scan base-package="com.sun"></context:component-scan>
</beans>

總結

使用註解之前要開啟自動掃描功能,其中base-package為需要掃描的包(含子包)。

<context:component-scan base-package=”cn.test”/>
@Configuration把一個類作為一個IoC容器,它的某個方法頭上如果註冊了@Bean,就會作為這個Spring容器中的Bean。
@Scope註解 作用域
@Lazy(true) 表示延遲初始化

@Component泛指元件,當元件不好歸類的時候,我們可以使用這個註解進行標註。

@Repository用於標註資料訪問元件,即DAO元件。

@Service用於標註業務層元件、

@Controller用於標註控制層元件(如struts中的action)

@Scope用於指定scope作用域的(用在類上)

@Autowired 預設按型別裝配,如果我們想使用按名稱裝配,可以結合@Qualifier註解一起使用。如下:
@Autowired @Qualifier(“personDaoBean”) 存在多個例項配合使用

@Resource預設按名稱裝配,當找不到與名稱匹配的bean才會按型別裝配。

@PostConstruct用於指定初始化方法(用在方法上)
@PreDestory用於指定銷燬方法(用在方法上)
@DependsOn:定義Bean初始化及銷燬時的順序
@Primary:自動裝配時當出現多個Bean候選者時,被註解為@Primary的Bean將作為首選者,否則將丟擲異常
@PostConstruct 初始化註解
@PreDestroy 摧毀註解 預設 單例 啟動就載入
@Async非同步方法呼叫

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章