摘要:從而能夠進一步深入了解框架。至此我們框架開發完成。雖然說閱讀源碼是了解框架的最終手段。但是框架作為一個生產框架,為了保證通用和穩定,源碼必定是高度抽象,且處理大量細節。下一篇文章應該會是徒手擼框架實現。
原文地址:https://www.xilidou.com/2018/...
Spring 作為 J2ee 開發事實上的標準,是每個Java開發人員都需要了解的框架。但是Spring 的 IoC 和 Aop 的特性,對于初級的Java開發人員來說還是比較難于理解的。所以我就想寫一系列的文章給大家講解這些特性。從而能夠進一步深入了解 Spring 框架。
讀完這篇文章,你將會了解:
什么是依賴注入和控制反轉
Ioc有什么用
Spring的 Ioc 是怎么實現的
按照Spring的思路開發一個簡單的Ioc框架
IoC 是什么?wiki百科的解釋是:
控制反轉(Inversion of Control,縮寫為IoC),是面向對象編程中的一種設計原則,可以用來減低計算機代碼之間的耦合度。其中最常見的方式叫做依賴注入(Dependency Injection,簡稱DI)。通過控制反轉,對象在被創建的時候,由一個調控系統內所有對象的外界實體,將其所依賴的對象的引用傳遞給它。也可以說,依賴被注入到對象中。Ioc 有什么用?
看完上面的解釋你一定沒有理解什么是 Ioc,因為是第一次看見上面的話也覺得云里霧里。
不過通過上面的描述我們可以大概的了解到,使用IoC的目的是為了解耦。也就是說IoC 是解耦的一種方法。
我們知道Java 是一門面向對象的語言,在 Java 中 Everything is Object,我們的程序就是由若干對象組成的。當我們的項目越來越大,合作的開發者越來越多的時候,我們的類就會越來越多,類與類之間的引用就會成指數級的增長。如下圖所示:
這樣的工程簡直就是災難,如果我們引入 Ioc 框架。由框架來維護類的生命周期和類之間的引用。我們的系統就會變成這樣:
這個時候我們發現,我們類之間的關系都由 IoC 框架負責維護類,同時將類注入到需要的類中。也就是類的使用者只負責使用,而不負責維護。把專業的事情交給專業的框架來完成。大大的減少開發的復雜度。
用一個類比來理解這個問題。Ioc 框架就是我們生活中的房屋中介,首先中介會收集市場上的房源,分別和各個房源的房東建立聯系。當我們需要租房的時候,并不需要我們四處尋找各類租房信息。我們直接找房屋中介,中介就會根據你的需求提供相應的房屋信息。大大提升了租房的效率,減少了你與各類房東之間的溝通次數。
Spring 的 IoC 是怎么實現的了解Spring框架最直接的方法就閱讀Spring的源碼。但是Spring的代碼抽象的層次很高,且處理的細節很高。對于大多數人來說不是太容易理解。我讀了Spirng的源碼以后以我的理解做一個總結,Spirng IoC 主要是以下幾個步驟。
1. 初始化 IoC 容器。 2. 讀取配置文件。 3. 將配置文件轉換為容器識別對的數據結構(這個數據結構在Spring中叫做 BeanDefinition) 4. 利用數據結構依次實例化相應的對象 5. 注入對象之間的依賴關系自己實現一個IoC框架
為了方便,我們參考 Spirng 的 IoC 實現,去除所有與核心原理無關的邏輯。極簡的實現 IoC 的框架。 項目使用 json 作為配置文件。使用 maven 管理 jar 包的依賴。
在這個框架中我們的對象都是單例的,并不支持Spirng的多種作用域。框架的實現使用了cglib 和 Java 的反射。項目中我還使用了 lombok 用來簡化代碼。
下面我們就來編寫 IoC 框架吧。
首先我們看看這個框架的基本結構:
從宏觀上觀察一下這個框架,包含了3個package、在包 bean 中定義了我們框架的數據結構。core 是我們框架的核心邏輯所在。utils 是一些通用工具類。接下來我們就逐一講解一下:
1. bean 定義了框架的數據結構BeanDefinition 是我們項目的核心數據結構。用于描述我們需要 IoC 框架管理的對象。
@Data @ToString public class BeanDefinition { private String name; private String className; private String interfaceName; private ListconstructorArgs; private List propertyArgs; }
包含了對象的 name,class的名稱。如果是接口的實現,還有該對象實現的接口。以及構造函數的傳參的列表 constructorArgs 和需要注入的參數列表 `propertyArgs。
2. 再看看我們的工具類包里面的對象:ClassUtils 負責處理 Java 類的加載,代碼如下:
public class ClassUtils { public static ClassLoader getDefultClassLoader(){ return Thread.currentThread().getContextClassLoader(); } public static Class loadClass(String className){ try { return getDefultClassLoader().loadClass(className); } catch (ClassNotFoundException e) { e.printStackTrace(); } return null; } }
我們只寫了一個方法,就是通過 className 這個參數獲取對象的 Class。
BeanUtils 負責處理對象的實例化,這里我們使用了 cglib 這個工具包,代碼如下:
public class BeanUtils { public staticT instanceByCglib(Class clz,Constructor ctr,Object[] args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(clz); enhancer.setCallback(NoOp.INSTANCE); if(ctr == null){ return (T) enhancer.create(); }else { return (T) enhancer.create(ctr.getParameterTypes(),args); } } }
ReflectionUtils 主要通過 Java 的反射原理來完成對象的依賴注入:
public class ReflectionUtils { public static void injectField(Field field,Object obj,Object value) throws IllegalAccessException { if(field != null) { field.setAccessible(true); field.set(obj, value); } } }
injectField(Field field,Object obj,Object value) 這個方法的作用就是,設置 obj 的 field 為 value。
JsonUtils 的作用就是為了解析我們的json配置文件。代碼比較長,與我們的 IoC 原理關系不大,感興趣的同學可以自行從github上下載代碼看看。
有了這幾個趁手的工具,我們就可以開始完成 Ioc 框架的核心代碼了。
3. 核心邏輯我的 IoC 框架,目前只支持一種 ByName 的注入。所以我們的 BeanFactory 就只有一個方法:
public interface BeanFactory { Object getBean(String name) throws Exception; }
然后我們實現了這個方法:
public class BeanFactoryImpl implements BeanFactory{ private static final ConcurrentHashMapbeanMap = new ConcurrentHashMap<>(); private static final ConcurrentHashMap beanDefineMap= new ConcurrentHashMap<>(); private static final Set beanNameSet = Collections.synchronizedSet(new HashSet<>()); @Override public Object getBean(String name) throws Exception { //查找對象是否已經實例化過 Object bean = beanMap.get(name); if(bean != null){ return bean; } //如果沒有實例化,那就需要調用createBean來創建對象 bean = createBean(beanDefineMap.get(name)); if(bean != null) { //對象創建成功以后,注入對象需要的參數 populatebean(bean); //再把對象存入Map中方便下次使用。 beanMap.put(name,bean; } //結束返回 return bean; } protected void registerBean(String name, BeanDefinition bd){ beanDefineMap.put(name,bd); beanNameSet.add(name); } private Object createBean(BeanDefinition beanDefinition) throws Exception { String beanName = beanDefinition.getClassName(); Class clz = ClassUtils.loadClass(beanName); if(clz == null) { throw new Exception("can not find bean by beanName"); } List constructorArgs = beanDefinition.getConstructorArgs(); if(constructorArgs != null && !constructorArgs.isEmpty()){ List
首先我們看到在 BeanFactory 的實現中。我們有兩 HashMap,beanMap 和 beanDefineMap。 beanDefineMap 存儲的是對象的名稱和對象對應的數據結構的映射。beanMap 用于保存 beanName和實例化之后的對象。
容器初始化的時候,會調用 BeanFactoryImpl.registerBean 方法。把 對象的 BeanDefination 數據結構,存儲起來。
當我們調用 getBean() 的方法的時候。會先到 beanMap 里面查找,有沒有實例化好的對象。如果沒有,就會去beanDefineMap查找這個對象對應的 BeanDefination。再利用DeanDefination去實例化一個對象。
對象實例化成功以后,我們還需要注入相應的參數,調用 populatebean()這個方法。在 populateBean 這個方法中,會掃描對象里面的Field,如果對象中的 Field 是我們IoC容器管理的對象,那就會調用 我們上文實現的 ReflectionUtils.injectField來注入對象。
一切準備妥當之后,我們對象就完成了整個 IoC 流程。最后這個對象放入 beanMap 中,方便下一次使用。
所以我們可以知道 BeanFactory 是管理和生成對象的地方。
4. 容器我們所謂的容器,就是對BeanFactory的擴展,負責管理 BeanFactory。我們的這個IoC 框架使用 Json 作為配置文件,所以我們容器就命名為 JsonApplicationContext。當然之后你愿意實現 XML 作為配置文件的容器你就可以自己寫一個 XmlApplicationContext,如果基于注解的容器就可以叫AnnotationApplcationContext。這些實現留個大家去完成。
我們看看 ApplicationContext 的代碼:
public class JsonApplicationContext extends BeanFactoryImpl{ private String fileName; public JsonApplicationContext(String fileName) { this.fileName = fileName; } public void init(){ loadFile(); } private void loadFile(){ InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName); ListbeanDefinitions = JsonUtils.readValue(is,new TypeReference >(){}); if(beanDefinitions != null && !beanDefinitions.isEmpty()) { for (BeanDefinition beanDefinition : beanDefinitions) { registerBean(beanDefinition.getName(), beanDefinition); } } } }
這個容器的作用就是 讀取配置文件。將配置文件轉換為容器能夠理解的 BeanDefination。然后使用 registerBean 方法。注冊這個對象。
至此,一個簡單版的 IoC 框架就完成。
5. 框架的使用我們寫一個測試類來看看我們這個框架怎么使用:
首先我們有三個對象
public class Hand { public void waveHand(){ System.out.println("揮一揮手"); } } public class Mouth { public void speak(){ System.out.println("say hello world"); } } public class Robot { //需要注入 hand 和 mouth private Hand hand; private Mouth mouth; public void show(){ hand.waveHand(); mouth.speak(); } }
我們需要為我們的 Robot 機器人注入 hand 和 mouth。
配置文件:
[ { "name":"robot", "className":"com.xilidou.framework.ioc.entity.Robot" }, { "name":"hand", "className":"com.xilidou.framework.ioc.entity.Hand" }, { "name":"mouth", "className":"com.xilidou.framework.ioc.entity.Mouth" } ]
這個時候寫一個測試類:
public class Test { public static void main(String[] args) throws Exception { JsonApplicationContext applicationContext = new JsonApplicationContext("application.json"); applicationContext.init(); Robot aiRobot = (Robot) applicationContext.getBean("robot"); aiRobot.show(); } }
運行以后輸出:
揮一揮手 say hello world Process finished with exit code 0
可以看到我們成功的給我的 aiRobot 注入了 hand 和 mouth。
至此我們 Ioc 框架開發完成。
總結這篇文章讀完以后相信你一定也實現了一個簡單的 IoC 框架。
雖然說閱讀源碼是了解框架的最終手段。但是 Spring 框架作為一個生產框架,為了保證通用和穩定,源碼必定是高度抽象,且處理大量細節。所以 Spring 的源碼閱讀起來還是相當困難。希望這篇文章能夠幫助理解 Spring Ioc 的實現。
下一篇文章 應該會是 《徒手擼框架--實現AOP》。
github 地址
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/68342.html
摘要:只實現了基于方法的攔截器。實現了一個遞歸的調用,直到執行完所有的攔截器。目標對象攔截器列表這個就是我們框架能夠理解的數據結構,這個時候問題就變成了對于哪個目標,增加哪些攔截器。 原文地址:犀利豆的博客 上一講我們講解了Spring 的 IoC 實現。大家可以去我的博客查看點擊鏈接,這一講我們繼續說說 Spring 的另外一個重要特性 AOP。之前在看過的大部分教程,對于Spring ...
摘要:我們就可以將這些請求合并,達到一定數量我們統一提交。總結一個比較生動的例子給大家講解了一些多線程的具體運用。學習多線程應該多思考多動手,才會有比較好的效果。地址徒手擼框架系列文章地址徒手擼框架實現徒手擼框架實現 原文地址:https://www.xilidou.com/2018/01/22/merge-request/ 在高并發系統中,我們經常遇到這樣的需求:系統產生大量的請求,但是這...
摘要:我們繼續看代碼的意思是這個是一段內嵌匯編代碼。也就是在語言中使用匯編代碼。就是匯編版的比較并交換。就是保證在多線程情況下,不阻塞線程的填充和消費。微觀上看匯編的是實現操作系統級別的原子操作的基石。 原文地址:https://www.xilidou.com/2018/02/01/java-cas/ CAS 是現代操作系統,解決并發問題的一個重要手段,最近在看 eureka 的源碼的時候。...
摘要:徒手擼一個簡單的框架之前在牛逼哄哄的框架,底層到底什么原理得知了遠程過程調用簡單來說就是調用遠程的服務就像調用本地方法一樣,其中用到的知識有序列化和反序列化動態代理網絡傳輸動態加載反射這些知識點。 徒手擼一個簡單的RPC框架 之前在牛逼哄哄的 RPC 框架,底層到底什么原理得知了RPC(遠程過程調用)簡單來說就是調用遠程的服務就像調用本地方法一樣,其中用到的知識有序列化和反序列化、動態...
摘要:先來看代碼吧,一會松哥再慢慢解釋關于這一段自動配置,解釋如下首先注解表明這是一個配置類。本文的案例,松哥已經上傳到上了,地址。我們使用 Spring Boot,基本上都是沉醉在它 Stater 的方便之中。Starter 為我們帶來了眾多的自動化配置,有了這些自動化配置,我們可以不費吹灰之力就能搭建一個生產級開發環境,有的小伙伴會覺得這個 Starter 好神奇呀!其實 Starter 也都...
閱讀 2119·2023-04-25 17:23
閱讀 2926·2021-11-17 09:33
閱讀 2524·2021-08-21 14:09
閱讀 3617·2019-08-30 15:56
閱讀 2614·2019-08-30 15:54
閱讀 1635·2019-08-30 15:53
閱讀 2138·2019-08-29 13:53
閱讀 1154·2019-08-29 12:31