懶人公司裡,所有人都非常的懶惰,唯獨他們的程式設計師還在拼命的奮鬥。這一天老闆想喝咖啡。
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 協議》,轉載必須註明作者和本文連結