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 id ="user" class ="com.kotlin.Bean.User" > </bean >
這裏可能會報一個沒有無參構造的錯,不過沒關係,這只是編譯器發現了我們用的是數據類,提示我們數據類是沒有無參構造的,但是我們用的noarg是在編譯時候纔會生成無參構造,所以編譯器是檢查不出來的
接下來我們就來寫一個測試類來測試下我們有沒有配置成功
class main
{
@Test
fun test()
{
val context = FileSystemXmlApplicationContext("src/main/webapp/WEB-INF/applicationContext.xml" )
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 id ="bean" class ="com.kotlin.utils.UserFactoryKt" factory-method ="getUser" > </bean >
進入測試類來測試一下
class main
{
@Test
fun test()
{
val context = FileSystemXmlApplicationContext("src/main/webapp/WEB-INF/applicationContext.xml" )
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 id ="bean2" class ="com.kotlin.utils.UserFactory" factory-method ="getUser" > </bean >
讓我們來運行下試試
也是可以的
第三種 實例工廠
這種方式也是最複雜,最少用的方式,大家看看就行了
我就直接在我們之前的UserFactory中寫了
class UserFactory
{
fun getUser2(): User
{
return User ("rose" , 28 )
}
}
創建完後,由於這方法並不是靜態的,所以我們還要配置下這個工廠
<bean id ="userFactory" class ="com.kotlin.utils.UserFactory" > </bean >
<bean id ="bean3" factory-bean ="userFactory" factory-method ="getUser2" > </bean >
運行下看看
結果毫無疑問
第二步 屬性注入
對於屬性的注入,spring中有兩種方式
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的配置
<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的配置
<bean id ="userDao" class ="com.kotlin.Dao.UserDao" > </bean >
<bean id ="userService" class ="com.kotlin.Service.UserService" >
<property name ="userDao" ref ="userDao" />
</bean >
然後進入我們的測試類,進行測試
class main
{
@Test
fun test()
{
val context = FileSystemXmlApplicationContext("src/main/webapp/WEB-INF/applicationContext.xml" )
val userService = context.getBean("userService" ) as UserService
userService.payMoney()
}
}
能夠注入對象,對數組,list等一些自然也是沒有問題的了,這些我就不一一展示了,我把代碼貼出來,大家看一下就行了
<bean id ="user" class ="com.kotlin.Bean.User" >
<property name ="array" >
<array >
<value > 張龍</value >
<value > 趙虎</value >
<value > 王朝</value >
<value > 馬漢</value >
</array >
</property >
<property name ="list" >
<list >
<value > 張三</value >
<value > 李四</value >
<value > 王二麻子</value >
</list >
</property >
<property name ="map" >
<map >
<entry key ="1" value ="a" />
<entry key ="2" value ="b" />
<entry key ="3" value ="c" />
</map >
</property >
<property name ="pro" >
<props >
<prop key ="driverclass" > com.mysql.jdbc.Driver</prop >
<prop key ="username" > root</prop >
</props >
</property >
</bean >
除此之外,還有一種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" >
<bean id ="user" class ="com.kotlin.Bean.User" p:name ="26" />
看到這邊,肯定有人覺得,着代碼量何止多了一點,簡直多的沒譜了。
確實,如果只能這樣寫的話,代碼量確實有些大,不過自然是有解決辦法的,那就是註解
第三步 使用註解
有了註解,我們的代碼量一下就少了很多了,下面看下用法就知道了
<context:component-scan base-package ="com.kotlin" > </context:component-scan >
這樣以來,我們只要寫一下註解,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()
{
val context = FileSystemXmlApplicationContext("src/main/webapp/WEB-INF/applicationContext.xml" )
val userService = context.getBean("userService" ) as UserService
userService.payMoney()
}
}
並沒有多幾句話,我們來試着運行一下
結果和之前一模一樣
現在我們來好好看一下註解
註解也分爲兩種,一種是在註解類的,一種是註解屬性的,和我們配置文件是一樣的
1. 註解類
註解類的註解有四種
@Controller : WEB層
@Service : 業務層
@Respository : 持久層
@Component
這些是爲了區分各個層次的。讓標註本身的用途更清晰,但其實,現在他們四個的功能是一樣的
你沒有看錯,這四個的功能是一樣的
這只是spring爲了區分各個層次和爲了未來版本的擴展準備的,所以我也建議大家還是記住並按這個去寫,萬一哪天spring更新了新功能,你也可以不需要做任何修改
2. 註解屬性
屬性的註解有兩種
@Autowired : 自動填裝
@Resource : 指定目標裝配
自動填裝 :根據你註釋的那個屬性的類去找被註解過的類 ,然後自動將其填充進去
指定目標裝配 :需要指定一個name,它將會根據你給的name去找對應的類,然後填充
最後,註解和xml配置是可以混合使用的,需要的時候我們也會把他們混一起使用,比如在xml裏面配置類,用註解填裝屬性