估計很多朋友使用 spring 很長時間,對於 spring 使用非常頻繁,實際上對於原始碼一直沒有靜下心來學習過。
你是不是有這樣的感覺呢?
但是 spring 原始碼存在一個問題,那就是過於抽象,導致學習起來成本上升。所以本文由淺入深,只實現 spring 的核心功能,便於自己和他人學習 spring 的核心原理。
文章有點長,耐心閱讀!如果對你有幫助,記得三連哦!
Spring 的核心就是 spring-beans,後面的一切 spring-boot,spring-cloud 都是建立在這個地基之上。
當別人問你 spring 的時候,希望你可以談談自己對於 spring ioc 自己更深層的見解,而不是網上人云亦云的幾句話。
另外本人整理收藏了20年多家公司面試知識點整理 ,以及各種Java核心知識點免費分享給大家,下方只是部分截圖
想要資料的話也可以點選直接進入:暗號:csdn,免費獲取。
控制反轉(Inversion of Control,縮寫為IoC),是物件導向程式設計中的一種設計原則,可以用來減低計算機程式碼之間的耦合度。
其中最常見的方式叫做依賴注入(Dependency Injection,簡稱DI)。
通過控制反轉,物件在被建立的時候,由一個調控系統內所有物件的外界實體,將其所依賴的物件的參照傳遞給它。
也可以說,依賴被注入到物件中。
IoC 是解耦的一種方法。
我們知道Java 是一門物件導向的語言,在 Java 中 Everything is Object,我們的程式就是由若干物件組成的。
當我們的專案越來越大,合作的開發者越來越多的時候,我們的類就會越來越多,類與類之間的參照就會成指數級的增長。
這樣的工程簡直就是災難,如果我們引入 Ioc 框架。
由框架來維護類的生命週期和類之間的參照。
我們的系統就會變成這樣:
這個時候我們發現,我們類之間的關係都由 IoC 框架負責維護類,同時將類注入到需要的類中。
也就是類的使用者只負責使用,而不負責維護。
把專業的事情交給專業的框架來完成,大大的減少開發的複雜度。
<dependency>
<groupId>com.github.houbb</groupId>
<artifactId>ioc</artifactId>
<version>0.1.11</version>
</dependency>
測試準備
全部測試程式碼,見 test 模組。
public class Apple {
public void color() {
System.out.println("Apple color: red. ");
}
}
類似於 xml 的設定,我們暫時使用 json 進行設定驗證。
[
{"name":"apple","className":"com.github.houbb.ioc.test.service.Apple"}
]
BeanFactory beanFactory = new JsonApplicationContext("apple.json");
Apple apple = (Apple) beanFactory.getBean("apple");
apple.color();
Apple color: red.
spring-beans 一切都是圍繞 bean 展開的。
BeanFactory 負責對 bean 進行生命週期的相關管理,本節展示第一小節的簡單實現流程。
Spring IoC 主要是以下幾個步驟。
初始化 IoC 容器。
讀取組態檔。
將組態檔轉換為容器識別對的資料結構(這個資料結構在Spring中叫做 BeanDefinition)
利用資料結構依次範例化相應的物件
注入物件之間的依賴關係
BeanDefinition 是 spring 對 java bean 屬性的一個抽象,經過這一層抽象,組態檔可以是 xml/json/properties/yaml 等任意一種,甚至包括註解掃包。
為 spring 的拓展帶來極大的靈活性。
本框架考慮到實現的簡單性,初步只實現了 json 和基於註解掃包兩種方式。
後期如果有時間可以考慮新增 xml 的實現,其實更多是 xml 的解析工作量,核心流程已經全部實現。
包含了對於 java bean 的基本資訊抽象。
其預設實現為 DefaultBeanDefinition.java,就是對介面實現的最基本的 java POJO
/**
* 物件定義屬性
* @author binbin.hou
* @since 0.0.1
*/
public interface BeanDefinition {
/**
* 名稱
* @return 名稱
* @since 0.0.1
*/
String getName();
/**
* 設定名稱
* @param name 名稱
* @since 0.0.1
*/
void setName(final String name);
/**
* 類名稱
* @return 類名稱
*/
String getClassName();
/**
* 設定類名稱
* @param className 類名稱
* @since 0.0.1
*/
void setClassName(final String className);
}
/**
* bean 工廠介面
* @author binbin.hou
* @since 0.0.1
*/
public interface BeanFactory {
/**
* 根據名稱獲取對應的範例資訊
* @param beanName bean 名稱
* @return 物件資訊
* @since 0.0.1
*/
Object getBean(final String beanName);
/**
* 獲取指定型別的實現
* @param beanName 屬性名稱
* @param tClass 型別
* @param <T> 泛型
* @return 結果
* @since 0.0.1
*/
<T> T getBean(final String beanName, final Class<T> tClass);
}
為介面最基礎的實現,原始碼如下:
/**
* bean 工廠介面
* @author binbin.hou
* @since 0.0.1
*/
public class DefaultBeanFactory implements BeanFactory {
/**
* 物件資訊 map
* @since 0.0.1
*/
private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
/**
* 物件 map
* @since 0.0.1
*/
private Map<String, Object> beanMap = new ConcurrentHashMap<>();
/**
* 註冊物件定義資訊
* @since 0.0.1
*/
protected void registerBeanDefinition(final String beanName, final BeanDefinition beanDefinition) {
// 這裡可以新增監聽器
this.beanDefinitionMap.put(beanName, beanDefinition);
}
@Override
public Object getBean(String beanName) {
Object bean = beanMap.get(beanName);
if(ObjectUtil.isNotNull(bean)) {
// 這裡直接返回的是單例,如果使用者指定為多例,則每次都需要新建。
return bean;
}
// 獲取對應設定資訊
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
if(ObjectUtil.isNull(beanDefinition)) {
throw new IocRuntimeException(beanName + " not exists in bean define.");
}
// 直接根據
Object newBean = createBean(beanDefinition);
// 這裡可以新增對應的監聽器
beanMap.put(beanName, newBean);
return newBean;
}
/**
* 根據物件定義資訊建立物件
* @param beanDefinition 物件定義資訊
* @return 建立的物件資訊
* @since 0.0.1
*/
private Object createBean(final BeanDefinition beanDefinition) {
String className = beanDefinition.getClassName();
Class clazz = ClassUtils.getClass(className);
return ClassUtils.newInstance(clazz);
}
@Override
@SuppressWarnings("unchecked")
public <T> T getBean(String beanName, Class<T> tClass) {
Object object = getBean(beanName);
return (T)object;
}
}
其中 ClassUtils 是基於 class 的反射工具類
基於 json 組態檔實現的基本實現,使用方式見開始種的例子程式碼。
/**
* JSON 應用上下文
* @author binbin.hou
* @since 0.0.1
*/
public class JsonApplicationContext extends DefaultBeanFactory {
/**
* 檔名稱
* @since 0.0.1
*/
private final String fileName;
public JsonApplicationContext(String fileName) {
this.fileName = fileName;
// 初始化設定
this.init();
}
/**
* 初始化設定相關資訊
*
* <pre>
* new TypeReference<List<BeanDefinition>>(){}
* </pre>
*
* 讀取檔案:https://blog.csdn.net/feeltouch/article/details/83796764
* @since 0.0.1
*/
private void init() {
InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName);
final String jsonConfig = FileUtil.getFileContent(is);
List<DefaultBeanDefinition> beanDefinitions = JsonBs.deserializeArray(jsonConfig, DefaultBeanDefinition.class);
if(CollectionUtil.isNotEmpty(beanDefinitions)) {
for (BeanDefinition beanDefinition : beanDefinitions) {
super.registerBeanDefinition(beanDefinition.getName(), beanDefinition);
}
}
}
}
至此,一個最基本的 spring ioc 就基本實現了。
學海無涯,我們一起勉力前行!
Ps:有需要的小夥伴可以點選直接進入:暗號:csdn,免費獲取。
面試專題檔案。
技術檔案
真實大廠面經