摘要:控制反轉和依賴注入的關系也已經清晰了,它們本質上可以說是一樣的,只是具體的關注點不同。我的博客地址參考資料控制反轉和依賴注入的理解那些年搞不懂的高深術語依賴倒置控制反轉依賴注入面向接口編程控制反轉和依賴注入
序
第一次了解到控制反轉(Inversion of Control)這個概念,是在學習Spring框架的時候。IOC和AOP作為Spring的兩大特征,自然是要去好好學學的。而依賴注入(Dependency Injection,簡稱DI)卻使得我困惑了挺久,一直想不明白他們之間的聯系。
控制反轉控制反轉顧名思義,就是要去反轉控制權,那么到底是哪些控制被反轉了?在2004年 Martin fowler 大神就提出了
“哪些方面的控制被反轉了?”
這個問題,他總結出是依賴對象的獲得被反轉了。
在單一職責原則的設計下,很少有多帶帶一個對象就能完成的任務。大多數任務都需要復數的對象來協作完成,這樣對象與對象之間就有了依賴。一開始對象之間的依賴關系是自己解決的,需要什么對象了就New一個出來用,控制權是在對象本身。但是這樣耦合度就非常高,可能某個對象的一點小修改就會引起連鎖反應,需要把依賴的對象一路修改過去。
如果依賴對象的獲得被反轉,具體生成什么依賴對象和什么時候生成都由對象之外的IOC容器來決定。對象只要在用到依賴對象的時候能獲取到就可以了,常用的方式有依賴注入和依賴查找(Dependency Lookup)。這樣對象與對象之間的耦合就被移除到了對象之外,后續即使有依賴修改也不需要去修改原代碼了。
總結一下,控制反轉是指把對象的依賴管理從內部轉移至外部。
依賴注入控制反轉是把對象之間的依賴關系提到外部去管理,可依賴是提到對象外面了,對象本身還是要用到依賴對象的,這時候就要用到依賴注入了。顧名思義,應用需要把對象所需要的依賴從外部注入進來。可以是通過對象的構造函數傳參注入,這種叫做構造器注入(Constructor Injection)。如果是通過JavaBean的屬性方法傳參注入,就叫做設值方法注入(Setter Injection)。
不管是通過什么方式注入的,如果是我們手動注入的話還是顯得太麻煩了。這時候就需要一個容器來幫我們實現這個功能,自動的將對象所需的依賴注入進去,這個容器就是前面提到的IOC容器了。
控制反轉和依賴注入的關系也已經清晰了,它們本質上可以說是一樣的,只是具體的關注點不同。控制反轉的關注點是控制權的轉移,而依賴注入則內含了控制反轉的意義,明確的描述了依賴對象在外部被管理然后注入到對象中。實現了依賴注入,控制也就反轉了。
例子首先是傳統的方式,耦合非常嚴重。
public class Main { public static void main(String[] args) { OrderService service = new OrderService(); service.test(); } }
public class OrderService { private OrderDao dao = new OrderDao(); public void test() { dao.doSomeThing(); } }
public class OrderDao { public void doSomeThing() { System.out.println("test"); } }
接下來是沒有使用容器的方式,松耦合了,但是手動注入非常的麻煩。
public class Main { public static void main(String[] args) { Dao dao = new OrderDao(); OrderService service = new OrderService(dao); service.test(); } }
public interface Dao { void doSomeThing(); }
public class OrderDao implements Dao { @Override public void doSomeThing() { System.out.println("test"); } }
public class OrderService { private Dao dao; public OrderService(Dao dao) { this.dao = dao; } public void test() { dao.doSomeThing(); } }
接下來使用容器造福人類。
// 引導類要放在項目根目錄下,也就是在 src 下面 public class Main { public static void main(String[] args) { // 生成容器 Container container = new Container(Main.class); // 獲取Bean OrderService service = container.getBean(OrderService.class); // 調用 service.test(); } }
@Component public class OrderService { @Autowired private Dao dao; public void test() { dao.doSomeThing(); } public Dao getDao() { return dao; } public void setDao(Dao dao) { this.dao = dao; } }
@Component public class OrderDao implements Dao { @Override public void doSomeThing() { System.out.println("test"); } }
public interface Dao { void doSomeThing(); }
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) public @interface Component { }
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD,ElementType.METHOD}) public @interface Autowired { }
public class Container { private List后記classPaths = new ArrayList<>(); private String separator; private Map components = new HashMap<>(); public Container(Class cls) { File file = new File(cls.getResource("").getFile()); separator = file.getName(); renderClassPaths(new File(this.getClass().getResource("").getFile())); make(); di(); } private void make() { classPaths.forEach(classPath -> { try { Class c = Class.forName(classPath); // 找到有 @ioc.Component 注解的類并實例化 if (c.isAnnotationPresent(Component.class)) { components.put(c, c.newInstance()); } } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) { e.printStackTrace(); } }); } /** * 注入依賴 */ private void di() { components.forEach((aClass, o) -> Arrays.stream(aClass.getDeclaredFields()).forEach(field -> { if (field.isAnnotationPresent(Autowired.class)) { try { String methodName = "set" + field.getType().getName().substring(field.getType().getName().lastIndexOf(".") + 1); Method method = aClass.getMethod(methodName, field.getType()); if (field.getType().isInterface()) { components.keySet().forEach(aClass1 -> { if (Arrays.stream(aClass1.getInterfaces()).anyMatch(aClass2 -> aClass2.equals(field.getType()))) { try { method.invoke(o, components.get(aClass1)); } catch (IllegalAccessException | InvocationTargetException e) { e.printStackTrace(); } } }); } else { method.invoke(o, components.get(field.getType())); } } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { e.printStackTrace(); } } })); } /** * 該方法會得到所有的類,將類的全類名寫入到classPaths中 * * @param file 包 */ private void renderClassPaths(File file) { if (file.isDirectory()) { File[] files = file.listFiles(); Arrays.stream(Objects.requireNonNull(files)).forEach(this::renderClassPaths); } else { if (file.getName().endsWith(".class")) { String classPath = file.getPath() .substring(file.getPath().lastIndexOf(separator) + separator.length() + 1) .replace("", ".") .replace(".class", ""); classPaths.add(classPath); } } } public T getBean(Class c) { return (T) components.get(c); } }
一些概念在腦海里總以為是清晰的,等實際用到或者是寫成文字的時候就發現有很多不理解的地方。本文的目的就是梳理下概念,做些記錄。這次自己嘗試實現了下IOC容器,一開始寫就知道自己之前的理解有問題了。好歹是寫出了個能用的版本,用來應付文章中的例子。后面可以去參考下Spring的實現,估計能學到不少東西。
我的博客地址
參考資料控制反轉和依賴注入的理解
那些年搞不懂的高深術語——依賴倒置?控制反轉?依賴注入?面向接口編程
控制反轉(IOC)和依賴注入(DI)
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/71476.html
摘要:對象之間耦合度過高的系統,必然會出現牽一發而動全身的情形。控制被反轉之后,獲得依賴對象的過程由自身管理變為了由容器主動注入。于是,他給控制反轉取了一個更合適的名字叫做依賴注入。 Spring還可以這么學--IoC(控制反轉) / DI(依賴注入)理解 聲明:文章的前三部分參考博文:https://www.cnblogs.com/Nouno...這篇文章首發是在我的個人微信訂閱號每天學編...
摘要:構造函數注入通過調用類的構造函數,將接口實現類通過構造函數變量傳入。而在中,其使用橫切技術,將這類代碼從原屬的封裝對象中提取出來,封裝到一個可重用模塊中,稱為。 最近實習用到Spring的開發框架,但是之前沒有接觸過,因此希望利用網上的資源來學習以下。 Spring官方給出了非常全面的介紹,非常適合我這種完全的小白……在這一系列學習中,我閱讀的主要資源是5.1.2 Reference ...
摘要:依賴注入是向某個類或方法注入一個值,其中所用到的原理就是控制反轉。但發現更多時間是在調和的源碼。里面就是從中取出這個,完成控制反轉的。控制反轉的優點最后來以我個人觀點談談控制反轉的優點吧。控制反轉為了降低項目耦合,提高延伸性。 本章開始來學習下Spring的源碼,看看Spring框架最核心、最常用的功能是怎么實現的。網上介紹Spring,說源碼的文章,大多數都是生搬硬推,都是直接看來的...
摘要:學習總結學習整理的一些筆記,很簡單。大部分認為和只是不同的叫法而已。依賴注入的兩種方式和注解使用注釋驅動的功能源碼剖析 Spring IoC學習總結 學習spring Ioc整理的一些筆記,很簡單。分享給大家。 IoC 基本概念 在這之前,我們先記住一句話。好萊塢原則:Dont call us, we will call you.其實這句話很恰當地形容了反轉的意味;Ioc, Inve...
摘要:使用的好處知乎的回答不用自己組裝,拿來就用。統一配置,便于修改。 前言 只有光頭才能變強 回顧前面: 給女朋友講解什么是代理模式 包裝模式就是這么簡單啦 單例模式你會幾種寫法? 工廠模式理解了沒有? 在刷Spring書籍的時候花了點時間去學習了單例模式和工廠模式,總的來說還是非常值得的! 本來想的是刷完《Spring 實戰 (第4版)》和《精通Spring4.x 企業應用開發實戰》...
閱讀 1121·2021-09-22 16:04
閱讀 1499·2019-08-30 15:43
閱讀 1109·2019-08-29 14:01
閱讀 3444·2019-08-26 12:19
閱讀 3359·2019-08-26 12:15
閱讀 1452·2019-08-26 12:13
閱讀 3270·2019-08-23 17:00
閱讀 1490·2019-08-23 15:38