第一次瞭解到控制反轉(Inversion of Control)這個概念,是在學習Spring
框架的時候。IOC
和AOP
做爲Spring
的兩大特徵,天然是要去好好學學的。而依賴注入(Dependency Injection,簡稱DI)卻使得我困惑了挺久,一直想不明白他們之間的聯繫。php
控制反轉顧名思義,就是要去反轉控制權,那麼究竟是哪些控制被反轉了?在2004年 Martin fowler
大神就提出了html
「哪些方面的控制被反轉了?」
這個問題,他總結出是依賴對象的得到被反轉了。git
在單一職責原則的設計下,不多有單獨一個對象就能完成的任務。大多數任務都須要複數的對象來協做完成,這樣對象與對象之間就有了依賴。一開始對象之間的依賴關係是本身解決的,須要什麼對象了就New
一個出來用,控制權是在對象自己。可是這樣耦合度就很是高,可能某個對象的一點小修改就會引發連鎖反應,須要把依賴的對象一路修改過去。github
若是依賴對象的得到被反轉,具體生成什麼依賴對象和何時生成都由對象以外的IOC容器
來決定。對象只要在用到依賴對象的時候能獲取到就能夠了,經常使用的方式有依賴注入和依賴查找(Dependency Lookup)。這樣對象與對象之間的耦合就被移除到了對象以外,後續即便有依賴修改也不須要去修改原代碼了。編程
總結一下,控制反轉是指把對象的依賴管理從內部轉移至外部。框架
控制反轉是把對象之間的依賴關係提到外部去管理,可依賴是提到對象外面了,對象自己仍是要用到依賴對象的,這時候就要用到依賴注入了。顧名思義,應用須要把對象所須要的依賴從外部注入進來。能夠是經過對象的構造函數傳參注入,這種叫作構造器注入(Constructor Injection)
。若是是經過JavaBean
的屬性方法傳參注入,就叫作設值方法注入(Setter Injection)
。ide
無論是經過什麼方式注入的,若是是咱們手動注入的話仍是顯得太麻煩了。這時候就須要一個容器來幫咱們實現這個功能,自動的將對象所需的依賴注入進去,這個容器就是前面提到的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<String> classPaths = new ArrayList<>(); private String separator; private Map<Class, Object> 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> T getBean(Class c) { return (T) components.get(c); } }
一些概念在腦海裏總覺得是清晰的,等實際用到或者是寫成文字的時候就發現有不少不理解的地方。本文的目的就是梳理下概念,作些記錄。此次本身嘗試實現了下IOC容器
,一開始寫就知道本身以前的理解有問題了。好歹是寫出了個能用的版本,用來應付文章中的例子。後面能夠去參考下Spring
的實現,估計能學到很多東西。ui