作者ntpuisbest (阿龍)
看板Soft_Job
標題[請益] Spring boot的依賴注入降低耦合的例子
時間Thu Mar 31 21:06:08 2022
推文有個連結有解答我的疑惑
感謝bron大
文章有點長
先說說我對依賴注入的理解
Spring boot
依賴注入大致有三種方式
透過建構子的 透過setter的 或是 field
這三種都可以透過@Autowired註解來達到依賴注入的效果
我自己想到的建構子的舉例是
假設有兩個類 Address 和 Employee好了
1.
public class Address {
String Country;
String City;
String Street;
public Address(String country, String city, String street) {
Country = country;
City = city;
Street = street;
}
}
2.
public class Employee {
String sex;
String name;
Address address;
// 沒有依賴注入的方式
public Employee(String Country,String City,String Street,String
sex, String name ) {
this.sex=sex;
this.address = new Address( Country, City,Street );
this.name=name;
}
// 有依賴注入的方式
public Employee(String sex, String name, Address address) {
this.sex = sex;
this.name = name;
this.address = address;
}
}
在上面的例子當中可以發現,如果哪一天
Address這個類新增了一個屬性叫 phoneNumber好了
沒有依賴注入的方式,必須要更改 Employee 的
this.address =new Address(Country,City,Street,phoneNumber)
而有依賴注入的方式確實降低了耦合
因為他不用更改Employee的建構方式
所以我理解依賴注入可以降低耦合
所以我理解依賴注入可以降低耦合
所以我理解依賴注入可以降低耦合
但我的問題是Spring boot 的 autowird annotation 有幫助我們降低耦合嗎
在常見的開發中 我們經常都會有 Dao 以及 Service
假設我有兩個 Dao 好了 分別是 Dao1 和 Dao2
以及一個Service
Dao1
public class Dao {
public void sayhi() {
System.out.println("hello");
}
}
Dao1
public class Dao {
public void sayhi() {
System.out.println("hello");
}
}
Dao2
public class Dao2 {
public void saygoodbye() {
System.out.println("say goodbye");
}
}
如果我不在service上面使用autowired
我的service會是
public class Service {
Dao1 dao=new Dao1();
Dao2 dao2=new Dao2();
public void sayhi() {
dao.sayhi();
}
public void saygoodbye() {
dao2.saygoodbye();
}
}
如果我使用了@Autowired註解
那我只是將
Dao1 dao=new Dao1();
Dao2 dao2=new Dao2();
替換成
@Autowired
Dao1 dao
@Autowired
Dao2 dao2
我想請問所以我使用了Autowired註解
我知道我可以不需要使用new 來建構實體
但 Spring 真的有幫我降低耦合嗎
即使我換成 setter 配合 autowired的方式好了
那個 setter也是要我自己去撰寫
Spring 幫我降低了耦合甚麼?
我的問題簡單來說就是
我知道依賴注入可以降低耦合
但Spring boot透過 @Autowired註解幫我降低耦合在哪
謝謝
p.s 因為面試的時候常常被面試官問說懂不懂甚麼是
控制反轉還有DI,我基本上舉例都舉 Address還有 Employee的例子
但當我反問下面例子的時候,他們好像也說要再回去想一下...
只有其中一個就說更複雜的例子會用到,但也沒說甚麼是更複雜的例子QQ
--
※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 118.167.157.11 (臺灣)
※ 文章網址: https://www.ptt.cc/bbs/Soft_Job/M.1648731970.A.382.html
※ 編輯: ntpuisbest (49.216.186.239 臺灣), 03/31/2022 21:14:38
推 Keade0325: 當有需要抽換實作的時候03/31 21:27
推 MoonCode: 在你理解前應該先完全不靠 spring 的功能,只靠 java03/31 21:29
→ MoonCode: 本身來做依賴注入,然後判斷有沒有真的有效就是你的類03/31 21:29
→ MoonCode: 可以把依賴的東西改用mock替換。那等你都弄好後開始不03/31 21:29
→ MoonCode: 斷的堆積業務邏輯時,就會發現初始化的地方會有一堆 n03/31 21:29
→ MoonCode: ew constructor 然後再傳進另一個 new constructor,那03/31 21:29
→ MoonCode: 這時候一個像 spring 這樣的框架就可以用各種方式來幫03/31 21:29
→ MoonCode: 助你避免自己寫這些 new,就可以避免一些麻煩。 但我是03/31 21:29
→ MoonCode: 喜歡自己手動操作啦,靠框架的話整個生命周期很難看懂03/31 21:29
→ MoonCode: 。03/31 21:29
推 wulouise: 我覺得任何DI framework都跟singleton 87%像邪惡03/31 21:33
推 illya65536: 個人覺得方便測試時去 mock,平常用 Laravel 的經驗03/31 21:35
→ bheegrl: Polymorphism03/31 21:39
→ bheegrl: 通常是autowired interface啦,再依參數配置決定要使用03/31 21:42
推 Keade0325: 簡單的例子就是更換DB driver03/31 21:43
→ bheegrl: 哪個實作上面那interface的component03/31 21:44
→ bronx0807: @Autowired只是依類名或型別幫你在Spring容器生物件03/31 22:00
→ bronx0807: DI與IoC才是低耦合的關鍵,與@Autowired無關03/31 22:01
→ ntpuisbest: 可是如果DI要我自己寫的話,Spring幫我做了啥,單純03/31 22:03
→ ntpuisbest: 的控制反轉有降低耦合嗎?03/31 22:03
推 bronx0807: Spring幫你new物件並注入到使用的對象屬性中 03/31 22:04
→ bronx0807: 還有上面Dao1 Dao2例子有誤,DI是從外面set進來03/31 22:05
→ ntpuisbest: 我的第一個例子應該是DI吧,把ADDRESS注入到Employee03/31 22:07
→ ntpuisbest: 當中03/31 22:07
→ ntpuisbest: 即使用Spring,不用自己寫new,可是建構子還是要自己寫03/31 22:08
→ ntpuisbest: 阿,降低了什麼功夫呢03/31 22:08
→ bronx0807: 幫你搞定層層的依賴關係03/31 22:10
→ bronx0807: 你可以試試不用Spring自己寫依賴注入,你就知道差異03/31 22:10
→ ntpuisbest: 我第一個例子就沒有依賴Spring 阿 地址跟員工那個03/31 22:13
→ foreverk: spring就是做掉你自己舉的例子,不然誰要往兩個class傳03/31 22:16
→ foreverk: dao進去?03/31 22:16
→ foreverk: 你後面舉的自己new dao的行為,就等於你前面沒有做DI的03/31 22:18
→ foreverk: 舉例了03/31 22:18
推 MoonCode: 我看下來只覺得原po寫太少了 哈哈哈哈03/31 22:19
→ foreverk: 今天是dao你感覺比較單純無法,如果你要new的是service03/31 22:24
→ foreverk: 呢?視你的專案複雜度,一個service可能會有10幾20個建03/31 22:24
→ foreverk: 構子需要你自己new出來,而service互相依賴不會只有有03/31 22:24
→ foreverk: 兩個這樣簡單的情況03/31 22:24
我有試著在Controller當中去new service
@RestController
public class Controller {
Service service=new Service( );
@GetMapping("test")
public void saysomething() {
service.saygoodbye();
service.sayhi();
}
}
即使Service 裡面需要20個DAO好了
在Controller裡面new Service不也一樣只要
一行
Service service=new Service( );
另外如果是setter 或是 Constructor方式的 DI
就我的理解 setter和建構子也是要自己寫
Spring 不會幫你產生
那我這樣看起來好像只是幫你從 new 換成了 @Autowired
這樣真的看不太出來 降低了甚麼耦合
因為建構子也是要自己寫啊
我覺得我好像陷入了泥淖中了QQ
翻了很多網頁,舉的例子大都跟我自己舉的 員工還有地址的差不多
※ 編輯: ntpuisbest (118.167.157.11 臺灣), 03/31/2022 22:36:56
→ bluelink: 可以看看跟qualifier的搭配03/31 22:33
→ kirin021: 要以類別與類別間的關係來看是否降低耦合吧?03/31 22:40
→ kirin021: 降低耦合的定義應該不是叫大家可以少寫code哈哈03/31 22:40
→ bronx0807: 不用@Autowired就要寫上面連結中那坨new03/31 22:41
→ foreverk: 20個dao只是一行嗎?那你試試new 20個都有20個dao的ser03/31 22:41
→ foreverk: vice03/31 22:41
→ foreverk: 其實有在寫mockito的應該都體會過,當service耦合太嚴03/31 22:43
→ foreverk: 重時,寫test有多痛苦,最後都會順便解耦XD03/31 22:43
推 atpx: 你寫工具給別人用比較會需要. 寫商業邏輯不太用的到03/31 22:44
推 abadfriend: 不確定 @Qualifier 能不能解答到你的疑惑?03/31 22:46
→ atpx: 運算的過程不想變動, 但又需要把產生的實例替換掉的情況下03/31 22:46
→ atpx: 就用到你的例子03/31 22:46
→ ntpuisbest: 謝謝bron大,我好像懂了,qualfied還在看@@03/31 22:48
推 abccbaandy: 不懂正常,因為例子太爛,簡單的new感受不到這東西的03/31 22:50
→ abccbaandy: 用處03/31 22:50
→ abccbaandy: 你試試"new"個jdbc connection就知道了03/31 22:52
→ foreverk: 會有spring好像沒幹事的錯覺,是因為作者是你自己,你03/31 22:52
→ foreverk: 可以掌控那些複雜的service建構子,當多人協作時有人需03/31 22:52
→ foreverk: 要使用你的service,還需要搞懂那些建構子的邏輯跟用法03/31 22:52
→ foreverk: ,那就是一場災難,更別提那個人的service立刻又跟那些03/31 22:52
→ foreverk: 建構子用途的物件耦合,每個人都這樣就沒完沒了03/31 22:52
推 lovdkkkk: 重點在 依賴於介面 而不依賴於實作03/31 23:22
→ lovdkkkk: 要能自己 new, 就綁死實作的 package/class 了03/31 23:23
→ lovdkkkk: 給 spring 生, 自己只綁介面定義 實作就可替換03/31 23:24
推 s06yji3: 降低耦合和少寫code是兩回事04/01 00:16
推 tttkkk: 整篇看完了 你要補的地方是 code to interface 的概念04/01 00:24
→ tttkkk: 先了解 interface 再來看 dependency injection 會較易懂04/01 00:25
→ tttkkk: 例如你有個 Dao interface 他有兩個實作 Dao1 和 Dao204/01 00:26
→ tttkkk: 用 DI 可以在你的 Service 裡面去指定用哪一個實作04/01 00:26
感謝上面所有大大
尤其是bron大,謝謝
ok 謝謝,我對介面的理解只有降低耦合
但跟di的關聯還沒補上
謝謝
※ 編輯: ntpuisbest (49.216.186.239 臺灣), 04/01/2022 00:35:55
推 tttkkk: 其實你舉的 service 例子不是解耦合喔 試想你若真的要在 04/01 00:39
→ tttkkk: service 中同時用到 Dao1 跟 Dao2 那怎會有解偶合的問題 04/01 00:39
→ tttkkk: 當你會需要偶爾使用Dao1 偶爾使用 Dao2 甚至未來改成Dao3 04/01 00:41
→ tttkkk: 才會需要解耦合 因為你把 Service 跟 Dao 解耦合了 04/01 00:42
→ tttkkk: 更改 Dao 實作時 就不會動到 Service 04/01 00:43
推 x246libra: 你第一個例子,地址,通常不會視為DI吧,比較像ddd的v 04/01 01:19
→ x246libra: alue object 04/01 01:19
→ lazarus1121: 我也有類似的問題,如果建構子會吃參數 04/01 01:38
→ lazarus1121: 如果要在spring上是不是最好改寫為另外set進去比較好 04/01 01:39
→ lazarus1121: 不然常常會寫一堆bean,好像沒有比較方便 04/01 02:01
→ lazarus1121: 但如果要用時再抓來set,物件的命名又會很混亂 04/01 02:07
推 handsomeLin: 一句話:DI並不解決耦合問題,DI只是幫助你更輕鬆的 04/01 02:36
→ handsomeLin: 測試並且轉換實作的時候不需要大量改動code 04/01 02:36
推 godsparticle: 樓上正解 04/01 08:30
→ foreverk: 不管是依賴介面別依賴實作,還是用DI,都是解決掉你的 04/01 08:43
→ foreverk: 耦合問題,才有後續帶來的輕鬆測試跟切換實作,避免大 04/01 08:43
→ foreverk: 量改動code,他們都是解耦過程的一部分 04/01 08:43
→ sharek: 會感覺不到DI好處,大概只能親身經歷過痛過強耦合的proje 04/01 09:22
→ sharek: ct才比較能體會 04/01 09:22
→ devilkool: 當你打算寫單元測試就會懂DI的好處了 04/01 09:29
→ ssccg: @Autowired找到完全同一個concrete class的bean來注入本來 04/01 10:25
→ ssccg: 就沒有降低耦合,可以找到相容的實作(實作interface的bean 04/01 10:27
→ ssccg: 或subclass的bean)才有降低耦合 04/01 10:27
推 tw11509: Autowired介面,搭配profile註解,可以在不同環境使用不 04/01 10:40
→ tw11509: 同實作 04/01 10:40
推 tw11509: 關於di,spring in action有個騎士出任務的範例,還蠻有 04/01 10:52
→ tw11509: 趣的 04/01 10:52
推 vi000246: 你可以A一下91哥的文 id是landlord 04/01 13:45
→ vi000246: 他有個土砲重構的範例滿不錯的 04/01 13:45
→ MonyemLi: spring 只是稍微降低耦合。主要降低的是多用介面,多寫 04/01 18:42
→ MonyemLi: 幾層 04/01 18:42
→ superpandal: 不想講但還是講一下好了 它就只是偵測你包內的 04/01 23:51
→ superpandal: annotation並且儲存起來(map) 需要的時候取用初始化 04/01 23:51
→ superpandal: 而已 依賴注入本身不是問題 問題是使用annotation的 04/01 23:52
→ superpandal: 依賴注入 專案大了以後 一堆初始化流程你都不見得能 04/01 23:53
→ superpandal: 夠掌握 更別說annotation設值所造成的影響 04/01 23:54
→ superpandal: 然後這樣的初始化效能肯定比你直接new來的差 04/01 23:55
→ superpandal: 介面的話更是扯 你直接改個類不就得了... 當然如果 04/01 23:57
→ superpandal: 習慣非常好 用annotation也都無所謂 但你不一定是一 04/01 23:57
→ superpandal: 一個人開發 04/01 23:57
→ superpandal: 用這些框架有時候就不是人在使用工具 是人被工具玩 04/02 00:00
→ superpandal: 更準確的是人寫工具的惡意 而非工具本身自主 04/02 00:01
→ superpandal: 無腦開發你才有人生 耗在這沒意義的事情才糟糕 04/02 00:06
→ ssccg: 樓上肯定沒用到request、session、refresh scope 04/02 10:32
→ ssccg: 才會覺得spring DI只是個map存起來這麼簡單 04/02 10:32
→ superpandal: 當然不是講的這麼大致方向 剩下的就只是應用再應用 04/02 22:42
→ superpandal: 講的這些真的要自己寫框架不難寫 04/02 22:44
→ superpandal: 只是個人不會遇到一個問題解決了再創造一個名詞 04/02 22:46
→ superpandal: 光看這些其實很容易眼花撩亂 還是學習真的知識比較好 04/02 22:48
推 CoNsTaR: Java 哈哈 Java 04/03 10:31
→ CoNsTaR: Old school try hard language 04/03 10:31
→ CoNsTaR: 搞笑的語言就是只能搞笑 04/03 10:31
推 tw11509: 去除掉springBoot,IoC、DI和DIP這些概念又不是Java獨有 04/03 21:17
→ tw11509: ,說Java搞笑跟這篇文章的關聯真是無法理解!? 04/03 21:17
推 CoNsTaR: 樓上找給我看除了 Java 以外還有哪個語言什麼事都要 anno 04/04 13:21
→ CoNsTaR: tations ,設定檔,框架魔法和 reflection 才能做的?真 04/04 13:21
→ CoNsTaR: 的笑死 04/04 13:21
推 tw11509: 嗯嗯,您說得對 04/04 21:15