Kotlin的Spring之旅(二):IOC控制反轉

IOC(控制反轉)

ioc是一種用來降低代碼耦合度的設計模式,一直以來都有很多方法來降低耦合度,而ioc是目前最有效最徹底的方法

ioc是基於xml配置的方式,使用反射的方式來實現類的創建。這樣可由IoC容器來管理對象的生命週期、依賴關係等,從而使得應用程序的配置和依賴性規範與實際的應用程序代碼分開

簡單來說,ioc就是把所有類都配置到了xml中,這樣一來,你需要使用一個類的時候就不需要new出來了,這意味着你可以根據需要隨意更改對象,而不需要修改任何一點源代碼

優點:

因爲把對象生成放在了XML裏定義,所以當我們需要換一個實現子類將會變成很簡單,只要修改XML就可以了,這樣我們甚至可以實現對象的熱插拔

缺點:

1. 代碼量稍有提升

2. 由於用了反射,效率也有所降低

雖然有不少缺點,但是對於現在項目最大的成本來源——維護 來說,這點缺點都不是問題,ioc將維護的成本大大降低,修改功能再也不需要去在茫茫多的源代碼裏面尋找一條條去改了,只要修改一下xml就可以了

下面就進入Spring中的IOC學習

第一步 配置bean(將類配置到xml中)

bean的實例化有三種方法

  • 使用類的無參構造創建
  • 使用靜態工廠創建
  • 使用實例工廠創建

首先第一種 無參構造

我們先創建一個Beans.kt,由於有了kotlin的數據類(data class),我們可以把所有的bean都寫在這個文件中

@Bean
data class User(var name: String, var age: Int)

首先創建一個User類,注意使用@Bean,不然數據類是沒有無參構造的,不知道這個是什麼的童鞋可以看一下我的上一篇,詳細介紹過了

然後進入我們的applicationContext.xml配置bean

<!-- 使用無參構造實現bean的創建 -->
<bean id="user" class="com.kotlin.Bean.User"></bean>

這裏可能會報一個沒有無參構造的錯,不過沒關係,這只是編譯器發現了我們用的是數據類,提示我們數據類是沒有無參構造的,但是我們用的noarg是在編譯時候纔會生成無參構造,所以編譯器是檢查不出來的

接下來我們就來寫一個測試類來測試下我們有沒有配置成功

class main
{
    @Test
    fun test()
    {
        //加載Spring配置文件(選擇你自己的配置文件位置)
        val context = FileSystemXmlApplicationContext("src/main/webapp/WEB-INF/applicationContext.xml")
        //創建對象(這裏就是你配置的id值了)
        val user = context.getBean("user") as User
        println(user)

    }
}

然後運行下,讓我們來看看結果

這裏寫圖片描述

如果顯示出了User那麼你就成功了

有小夥伴要問了,要是有參數怎麼辦,其實只需要在bean標籤下加上constructor-arg標籤就可以進行配置了,不過不急,我們後面會一一道來

第二種 靜態工廠

這時候就有一個問題了,在kotlin中是沒有靜態類這個東西的,不過並不要緊,因爲這種配置方式本來就很少用

如果非要寫的話,也不是不可以,kotlin中雖然沒有靜態類,但是肯定是有差不多的東西的不是麼

kotlin中有兩種代替靜態類的方法

  • 包級函數
  • 伴生對象

下面我們就一種種試試

1. 包級函數

先創建一個UserFactory.kt,然後來一個getUser()

inline fun getUser(): User
{
    return User("mike", 13)
}

然後配置bean

<!-- 用包級函數實現靜態工廠方式構建bean -->
<bean id="bean" class="com.kotlin.utils.UserFactoryKt" factory-method="getUser"></bean>

進入測試類來測試一下

class main
{
    @Test
    fun test()
    {
        //加載Spring配置文件,創建對象
        val context = FileSystemXmlApplicationContext("src/main/webapp/WEB-INF/applicationContext.xml")
        //只需要改變一下id就行了
        val user = context.getBean("bean") as User
        println(user)

    }
}

最後來看看,可以麼

這裏寫圖片描述

不出意外,成功了

2. 伴生對象

就在UserFactory.kt中,我們來寫一個UserFactory類

class UserFactory
{
    companion object
    {
        @JvmStatic
        fun getUser(): User
        {
            return User("jack", 35)
        }
    }
}

我們可以看到我不僅寫了一個getUser還加了一個註釋,因爲畢竟不是靜態方法,用起來時候還是有點不一樣的,而這個註釋就可以幫助我們把這些不同全部抹去

(具體的我就不說了,畢竟不是專門說kotlin的,有興趣的小夥伴可以在java文件中調用一下我們寫的這個方法,看看加和不加有什麼區別)

配置一下bean

<!-- 用伴生對象實現靜態工廠方式構建bean -->
<bean id="bean2" class="com.kotlin.utils.UserFactory" factory-method="getUser"></bean>

讓我們來運行下試試

這裏寫圖片描述

也是可以的

第三種 實例工廠

這種方式也是最複雜,最少用的方式,大家看看就行了

我就直接在我們之前的UserFactory中寫了

class UserFactory
{
    fun getUser2(): User
    {
        return User("rose", 28)
    }
}

創建完後,由於這方法並不是靜態的,所以我們還要配置下這個工廠

<!-- 使用實例工廠創建bean對象 -->
    <!-- 首先創建工廠對象 -->
    <bean id="userFactory" class="com.kotlin.utils.UserFactory"></bean>
    <bean id="bean3" factory-bean="userFactory" factory-method="getUser2"></bean>

運行下看看

這裏寫圖片描述

結果毫無疑問

第二步 屬性注入

對於屬性的注入,spring中有兩種方式

  • 通過有參構造注入
  • set方法注入

1. 有參構造注入

對象我們就用之前的那個User,就不改了,直接進入bean的配置

<!--有參構造注入屬性值-->
    <bean id="user" class="com.kotlin.Bean.User">
        <!--配置屬性值-->
        <constructor-arg name="name" value="tom"/>
        <constructor-arg name="age" value="35"/>
    </bean>

之前也提了下,使用constructor-arg進行參數的賦值,然後就運行下看看吧

這裏寫圖片描述

結果顯示注入成功

2. set方法注入

在我們平時的使用中,這個是我們用的最多的注入方法

由於kotlin的數據類自帶了get和set方法,我們就不用自己手寫了,直接進入bean的配置

<!--set方法注入-->
    <bean id="user2" class="com.kotlin.Bean.User">
        <property name="name" value="barry"/>
        <property name="age" value="23"/>
    </bean>

修改id,點擊運行

這裏寫圖片描述

結果出來了

這時可能大家會想,那我要注入一個對象怎麼辦呢?

這個其實差不多,只是注入的從一個具體值變成了一個引用

這次我們稍微寫完整一點,首先創建UserDao,而這個UserDao中有一個payMoney方法

@Bean
data class UserDao(var name : String)
{
    fun payMoney()
    {
        println("pay money")
    }
}

你可以直接把UserDao定義爲數據類,這樣會省事很多

然後再定義一個UserService,構造函數爲UserDao

@Bean
data class UserService(var userDao: UserDao)
{
    fun payMoney()
    {
        userDao.payMoney()
        println("service pay money")
    }
}

Servlet就暫時用我們的測試函數來代替,然後就進行bean的配置

<!--對象注入-->
<!--配置UserDao-->
<bean id="userDao" class="com.kotlin.Dao.UserDao"></bean>
<bean id="userService" class="com.kotlin.Service.UserService">
    <!--name爲UserService中屬性的名字-->
    <!--由於數據類的特殊性,用constructor-arg和property都是可以的-->
    <!--<constructor-arg name="userDao" ref="userDao"/>-->
    <property name="userDao" ref="userDao"/>
</bean>

然後進入我們的測試類,進行測試

class main
{
    @Test
    fun test()
    {
        //加載Spring配置文件,創建對象
        val context = FileSystemXmlApplicationContext("src/main/webapp/WEB-INF/applicationContext.xml")
        //service的id
        val userService = context.getBean("userService") as UserService
        userService.payMoney()
    }
}

這裏寫圖片描述

能夠注入對象,對數組,list等一些自然也是沒有問題的了,這些我就不一一展示了,我把代碼貼出來,大家看一下就行了

<!--注入複雜類型屬性-->
    <bean id="user" class="com.kotlin.Bean.User">
        <!--array-->
        <property name="array">
            <array>
                <value>張龍</value>
                <value>趙虎</value>
                <value>王朝</value>
                <value>馬漢</value>
            </array>
        </property>
        <!--list-->
        <property name="list">
            <list>
                <value>張三</value>
                <value>李四</value>
                <value>王二麻子</value>
            </list>
        </property>
        <!--map-->
        <property name="map">
            <map>
                <entry key="1" value="a"/>
                <entry key="2" value="b"/>
                <entry key="3" value="c"/>
            </map>
        </property>
        <!--properties-->
        <property name="pro">
            <props>
                <prop key="driverclass">com.mysql.jdbc.Driver</prop>
                <prop key="username">root</prop>
            </props>
        </property>
    </bean>

除此之外,還有一種p名稱空間注入,這種方式也蠻簡單的,這裏貼出來,大家看一看

<!-- 只需要在這裏加一個 xmlns:p="http://www.springframework.org/schema/p"-->
<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"
       xmlns:p="http://www.springframework.org/schema/p"
       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.xsd">

<!-- 這裏就可以直接用  p:屬性名="屬性值" 的方式來注入 -->
<bean id="user" class="com.kotlin.Bean.User" p:name="26"/>

看到這邊,肯定有人覺得,着代碼量何止多了一點,簡直多的沒譜了。

確實,如果只能這樣寫的話,代碼量確實有些大,不過自然是有解決辦法的,那就是註解

第三步 使用註解

有了註解,我們的代碼量一下就少了很多了,下面看下用法就知道了

<!--註解掃描-->
<!--1.到包中掃描類,方法,屬性上面的註解-->
<context:component-scan base-package="com.kotlin"></context:component-scan>

<!--2.只掃描屬性上面的註解-->
<!-- <context:annotation-config ></context:annotation-config> -->

這樣以來,我們只要寫一下註解,xml中只留一句話就行了。一般情況下我們都用上面那一種

我們先看一下例子,還是原來的那些,只是我們加上註解

@Bean
@Repository(value = "userDao")
data class UserDao(var name : String)
{
    fun payMoney()
    {
        println("dao pay money")
    }
}
@Bean
@Service(value = "userService")
data class UserService(@Resource(name = "userDao")var userDao: UserDao)
{
    fun payMoney()
    {
        userDao.payMoney()
        println("service pay money")
    }
}
class main
{
    @Test
    fun test()
    {
        //加載Spring配置文件,創建對象
        val context = FileSystemXmlApplicationContext("src/main/webapp/WEB-INF/applicationContext.xml")
        //service的id
        val userService = context.getBean("userService") as UserService
        userService.payMoney()
    }
}

並沒有多幾句話,我們來試着運行一下

這裏寫圖片描述

結果和之前一模一樣

現在我們來好好看一下註解

註解也分爲兩種,一種是在註解類的,一種是註解屬性的,和我們配置文件是一樣的

1. 註解類

註解類的註解有四種

  • @Controller : WEB層
  • @Service : 業務層
  • @Respository : 持久層
  • @Component

這些是爲了區分各個層次的。讓標註本身的用途更清晰,但其實,現在他們四個的功能是一樣的

你沒有看錯,這四個的功能是一樣的

這只是spring爲了區分各個層次和爲了未來版本的擴展準備的,所以我也建議大家還是記住並按這個去寫,萬一哪天spring更新了新功能,你也可以不需要做任何修改

2. 註解屬性

屬性的註解有兩種

  • @Autowired : 自動填裝
  • @Resource : 指定目標裝配

自動填裝根據你註釋的那個屬性的類去找被註解過的類,然後自動將其填充進去

指定目標裝配 :需要指定一個name,它將會根據你給的name去找對應的類,然後填充

最後,註解和xml配置是可以混合使用的,需要的時候我們也會把他們混一起使用,比如在xml裏面配置類,用註解填裝屬性

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章