新四季網

java如何使用io流獲取響應的數據(java手動實現一個IOC)

2023-04-17 05:25:18

面試官特別愛問SpringIOC底層實現,Spring源碼晦澀難懂 怎麼辦呢? 跟著老師手動實現一個mini ioc容器吧,實現後再回頭看Spring源碼事半功倍哦~,就算直接和面試官講也完全可以哦,類名完全按照源碼設計,話不多說 開幹~!

手動實現IOC容器的設計需要實現的IOC功能:可以通過xml配置bean信息可以通過容器getBean獲取對象能夠根據Bean的依賴屬性實現依賴注入可以配置Bean的單例多例實現簡易IOC設計的類

類之間關係模型

前期準備

創建maven項目引入依賴

dom4j dom4j 1.1 commons-beanutils commons-beanutils 1.9.3

準備3個bean的實體類

/** * 學生類 * 學生類依賴班級對象 * 並提供 sayHello 方法 * @作者 itcast * @創建日期 2020/3/7 19:46 **/public class Student { private String name; private TClass tClass; public void sayHello{ System.out.println("大家好,我是" this.name " 我的班級是==>" tClass.getCname " 我的老師是" tClass.getTeacher.getTname); } public String getName { return name; } public void setName(String name) { this.name = name; } public TClass gettClass { return tClass; } public void settClass(TClass tClass) { this.tClass = tClass; }}/** * 班級類 * 班級類依賴教師對象 * @作者 itcast * @創建日期 2020/3/7 19:45 **/public class TClass { private String cname;// 班級名稱 private Teacher teacher; // 老師 public String getCname { return cname; } public void setCname(String cname) { this.cname = cname; } public com.itcast.ioc.bean.Teacher getTeacher { return teacher; } public void setTeacher(com.itcast.ioc.bean.Teacher teacher) { this.teacher = teacher; }}/** * 教師類 * @作者 itcast * @創建日期 2020/3/7 19:44 **/public class Teacher { private String tname;// 老師名稱 public String getTname { return tname; } public void setTname(String tname) { this.tname = tname; }}

xml配置對象

配置學生對象: 小明

依賴班級對象: 3年2班

依賴教師對象: 陳老師

mini-IOC容器-定義類定義BeanFactory

/** * 容器的基礎接口 * 提供容器最基本的功能 */public interface BeanFactory { // 核心方法 獲取對象 Object getBean(String beanName);}

定義DefaultListableBeanFactory

package com.itcast.ioc.core;import java.util.Map;import java.util.concurrent.ConcurrentHashMap;/** * 基礎容器的核心實現 * 提供 beanDefinitionMap 存儲bean的定義 * 提供 singletonObjects 存儲bean的對象實例 * @作者 itcast * @創建日期 2020/7/8 15:37 **/public class DefaultListableBeanFactory implements BeanFactory { // 提供 beanDefinitionMap 存儲bean的定義 private Map beanDefinitionMap = new ConcurrentHashMap; // 提供 singletonObjects 存儲bean的對象實例 (scope為singleton的) private Map singletonObjects = new ConcurrentHashMap; /** * 實現getBean方法 * @param beanName * @return */ @Override public Object getBean(String beanName) { return null; } /** * 將bean註冊到容器中 * @param beanDefinition */ public void registerBeanDefinition(BeanDefinition beanDefinition){ beanDefinitionMap.put(beanDefinition.getBeanName,beanDefinition); }}

定義BeanDefnition

/** * 用於描述Bean的定義 * @作者 itcast * @創建日期 2020/7/8 15:41 **/public class BeanDefinition { private String beanName; // bean標籤的ID 作為bean的唯一標識 private String className; // bean的所屬class private String scope = "singleton"; // bean的scope作用域 private List propertyList = new ArrayList; public String getBeanName { return beanName; } public void setBeanName(String beanName) { this.beanName = beanName; } public String getClassName { return className; } public void setClassName(String className) { this.className = className; } public String getScope { return scope; } public void setScope(String scope) { this.scope = scope; } public List getPropertyList { return propertyList; } public void setPropertyList(List propertyList) { this.propertyList = propertyList; }}

定義Property

/** * 用於封裝一個property標籤 * 屬性數據 * @作者 itcast * @創建日期 2020/7/8 15:44 **/public class Property { private String name; // 屬性名稱 private String value; // 屬性的值 private String ref; // 屬性的引用 public String getName { return name; } public void setName(String name) { this.name = name; } public String getValue { return value; } public void setValue(String value) { this.value = value; } public String getRef { return ref; } public void setRef(String ref) { this.ref = ref; }}

定義XmlBeanFactory

/** * 繼承核心實現類 * 基於xml配置bean的實現類 * @作者 itcast * @創建日期 2020/7/8 15:47 **/public class XmlBeanFactory extends DefaultListableBeanFactory { /** * 將解析配置文件 註冊bean的所有工作交給reader對象 */ final XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(this); /** * 構造器需要傳入xml配置文件 * @param configPath */ public XmlBeanFactory(String configPath) { // 使用reader對象 解析配置 註冊Bean this.xmlBeanDefinitionReader.loadBeanDefinitions(configPath); }}

定義XmlBeanDefinitionReader

/** * 解析配置 * 註冊到容器中 * @作者 itcast * @創建日期 2020/7/8 15:51 **/public class XmlBeanDefinitionReader { // 核心beanfactory對象 用於將解析後的bean註冊到beanfactory中 final DefaultListableBeanFactory beanFactory; public XmlBeanDefinitionReader(DefaultListableBeanFactory beanFactory) { this.beanFactory = beanFactory; } /** * 根據傳遞的配置文件 * 解析配置 * 註冊bean * @param configPath */ void loadBeanDefinitions(String configPath){ }}

mini-IOC容器--解析註冊實現步驟

1. 通過dom4j解析xml得到Document文檔 2. 遍歷文檔所有Bean標籤 3. 解析每一個Bean標籤 封裝一個BeanDefinition對象 4. 解析每一個Bean標籤下的所有Property標籤 封裝一個Property對象 5. 將BeanDefinition和Property對象註冊到容器

實現xml解析及bean註冊

/** * 解析配置 * 註冊到容器中 * @作者 itcast * @創建日期 2020/7/8 15:51 **/public class XmlBeanDefinitionReader { // 核心beanfactory對象 用於將解析後的bean註冊到beanfactory中 final DefaultListableBeanFactory beanFactory; public XmlBeanDefinitionReader(DefaultListableBeanFactory beanFactory) { this.beanFactory = beanFactory; } /** * 根據傳遞的配置文件 * 解析配置 * 註冊bean * @param configPath */ void loadBeanDefinitions(String configPath){ // 1. 通過dom4j解析xml得到Document文檔 Document document = doLoadDocument(configPath); // 2. 遍歷文檔所有Bean標籤 Element rootElement = document.getRootElement; List list = rootElement.selectNodes("//bean"); for (Element element : list) { // 3. 解析每一個Bean標籤 封裝一個BeanDefinition對象 BeanDefinition beanDefinition = parseBeanDefinition(element); // 5. 將BeanDefinition和Property對象註冊到容器 beanFactory.registerBeanDefinition(beanDefinition); } } /** * 3. 解析每一個Bean標籤 封裝一個BeanDefinition對象 * 4. 解析每一個Bean標籤下的所有Property標籤 封裝一個Property對象 */ BeanDefinition parseBeanDefinition(Element element){ BeanDefinition beanDefinition = new BeanDefinition; String beanName = element.attributeValue("id"); String className = element.attributeValue("class"); String scope = element.attributeValue("scope"); beanDefinition.setBeanName(beanName); beanDefinition.setClassName(className); if(scope!=null&&!"".equals(scope)){ beanDefinition.setScope(scope); } List propertyList = element.elements("property"); for (Element propertyEle : propertyList) { Property property = new Property; property.setName(propertyEle.attributeValue("name")); property.setValue(propertyEle.attributeValue("value")); property.setRef(propertyEle.attributeValue("ref")); beanDefinition.getPropertyList.add(property); } return beanDefinition; } /** * 解析Document文檔 * @param configPath * @return */ Document doLoadDocument(String configPath){ InputStream inputStream = this.getClass.getClassLoader.getResourceAsStream(configPath); SAXReader saxReader = new SAXReader; try { return saxReader.read(inputStream); } catch (DocumentException e) { e.printStackTrace; System.out.println("解析xml出現異常==>" e.getMessage); throw new RuntimeException(e.getMessage); } }}

準備測試類

/** * 測試類 * @作者 itcast * @創建日期 2020/7/8 16:19 **/public class IocTest { public static void main(String[] args) { // 創建IOC容器 BeanFactory beanFactory = new XmlBeanFactory("applicationContext.xml"); // 通過容器獲取對象 Student student = (Student)beanFactory.getBean("student"); // 調用對象sayHello方法 student.sayHello; }}

斷點查看註冊情況

可以看到我們配置的xml內容 已經解析成了BeanDefinition對象,註冊到了核心容器的map中

mini-IOC容器-getBean實現步驟

1. 先從單例的map集合中獲取 是否有指定beanName的對象 有直接返回 沒有下一步2. 從註冊集合中獲取bean的定義對象 有下一步 沒有拋異常NoSuchBeanDefinition3. 判斷bean的scope作用域 singleton單例 createBean對象 存入單例集合 返回對象 prototype多例 createBean對象 返回對象4. createBean方法 獲取BeanDefinition中的className 通過反射API得到Class對象 通過反射API得到bean實例 獲取BeanDefinition中依賴的屬性列表 實現屬性的依賴注入

實現getBean及createBean方法

/** * 基礎容器的核心實現 * 提供 beanDefinitionMap 存儲bean的定義 * 提供 singletonObjects 存儲bean的對象實例 * @作者 itcast * @創建日期 2020/7/8 15:37 **/public class DefaultListableBeanFactory implements BeanFactory { // 提供 beanDefinitionMap 存儲bean的定義 private Map beanDefinitionMap = new ConcurrentHashMap; // 提供 singletonObjects 存儲bean的對象實例 (scope為singleton的) private Map singletonObjects = new ConcurrentHashMap; /** * 實現getBean方法 * @param beanName * @return */ @Override public Object getBean(String beanName) {// 1. 先從單例的map集合中獲取 是否有指定beanName的對象 Object singletonObj = singletonObjects.get(beanName);// 有直接返回 if(singletonObj!=null){ return singletonObj; }// 沒有下一步// 2. 從註冊集合中獲取bean的定義對象 BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);// 有下一步// 沒有拋異常NoSuchBeanDefinition if(beanDefinition==null){ throw new RuntimeException("NoSuchBeanDefinition : 你找的 " beanName " 對象 不存在"); }// 3. 判斷bean的scope作用域 String scope = beanDefinition.getScope;// singleton單例 if("singleton".equals(scope)){// createBean對象 Object obj = createBean(beanDefinition);// 存入單例集合 singletonObjects.put(beanName,obj);// 返回對象 return obj; }else {// prototype多例// createBean對象 return createBean(beanDefinition);// 返回對象 } } /** * //4. createBean方法 * //獲取BeanDefinition中的className * //通過反射API得到Class對象 * //通過反射API得到bean實例 * //獲取BeanDefinition中依賴的屬性列表 * //實現屬性的依賴注入 * 創建對象 * @param beanDefinition * @return */ Object createBean(BeanDefinition beanDefinition){ String className = beanDefinition.getClassName; Class aClass; try { aClass = Class.forName(className); } catch (ClassNotFoundException e) { e.printStackTrace; throw new RuntimeException("類未找到" e.getMessage); } // 創建對象: Object obj; try { obj = aClass.newInstance; } catch (InstantiationException e) { e.printStackTrace; throw new RuntimeException("創建對象失敗" e.getMessage); } catch (IllegalAccessException e) { e.printStackTrace; throw new RuntimeException("非法訪問" e.getMessage); } // 依賴注入 List propertyList = beanDefinition.getPropertyList; for (Property property : propertyList) { String name = property.getName; String value = property.getValue; String ref = property.getRef; // 屬性名不為空 進行注入 if(name!=null&&!"".equals(name)){ // 如果配置的是value值 直接注入 if(value!=null&&!"".equals(value)){ Map params = new HashMap; params.put(name,value); try { BeanUtils.populate(obj,params); } catch (IllegalAccessException e) { e.printStackTrace; throw new RuntimeException("非法訪問" e.getMessage); } catch (InvocationTargetException e) { e.printStackTrace; throw new RuntimeException("調用目標對象失敗" e.getMessage); } } // 如果配置的是ref需要獲取其它對象注入 if(ref!=null&&!"".equals(ref)){ try { BeanUtils.setProperty(obj,name,getBean(ref)); } catch (IllegalAccessException e) { e.printStackTrace; throw new RuntimeException("非法訪問" e.getMessage); } catch (InvocationTargetException e) { e.printStackTrace; throw new RuntimeException("調用目標對象失敗" e.getMessage); } } } } return obj; } /** * 將bean註冊到容器中 * @param beanDefinition */ public void registerBeanDefinition(BeanDefinition beanDefinition){ beanDefinitionMap.put(beanDefinition.getBeanName,beanDefinition); }}

mini-IOC容器-單例對象初始化DefaultListableBeanFactory增加初始化方法

public void preInstaniceSingletons{ beanDefinitionMap.forEach((beanName,beanDefinition)->{ String scope = beanDefinition.getScope; // 判斷單例 非抽象 不懶加載 if("singleton".equals(scope)){ this.getBean(beanName); } }); }

XmlBeanFactory增加單例對象初始化

public XmlBeanFactory(String configPath) { // 使用reader對象 解析配置 註冊Bean this.xmlBeanDefinitionReader.loadBeanDefinitions(configPath); // 初始化單例對象 this.preInstaniceSingletons;}

mini-IOC容器-測試和小結

測試對象能否獲取

查看bean的註冊及單例集合信息

可以通過變更scope的值查看對應的變化

IOC容器源碼及其它面試細節

擴展: 容器如何創建對象

IOC容器在準備創建對象時, 會判斷是否有配置 factory-method方法

如果有配置 會調用factory-method所指向的方法構建對象.

如果沒配置,會檢查是否有配置構造參數

無構造參數: 調用默認構造器創建對象

有構造參數: 根據參數情況匹配對應的構造器

擴展: bean的生命周期

spring 容器中的bean的完整生命周期一共分為十一步完成。

1.bean對象的實例化2.封裝屬性,也就是設置properties中的屬性值3.如果bean實現了BeanNameAware,則執行setBeanName方法,也就是bean中的id值4.如果實現BeanFactoryAware或者ApplicationContextAware ,需要設置setBeanFactory或者上下文對象setApplicationContext5.如果存在類實現BeanPostProcessor後處理bean,執行postProcessBeforeInitialization,可以在初始化之前執行一些方法6.如果bean實現了InitializingBean,則執行afterPropertiesSet,執行屬性設置之後的操作7.調用執行指定的初始化方法8.如果存在類實現BeanPostProcessor則執行postProcessAfterInitialization,執行初始化之後的操作9.執行自身的業務方法10.如果bean實現了DisposableBean,則執行spring的的銷毀方法11.調用執行自定義的銷毀方法。

擴展: bean的循環依賴問題

A 依賴 B B 依賴 A 產生閉環,稱為循環依賴

Spring 默認允許單例對象的屬性注入 所產生的循環依賴

單例對象的循環依賴 Spring通過3級緩存來解決 比如一個類A中有一個屬性是B類,B類中有一個屬性是A類,這時看Spring是怎麼解決他們的相互依賴的。Spring注入一個類的大體步驟分為兩部分,一是先完成對類的構造工作,二是會對類的屬性進行設置和填充。首先Spring構造A類,通過AbstractAutowireCapableBeanFactory的doCreateBean方法中調用addSingletonFactory方法將A類曝光到singletonFactories中。

這時完成A的構造後,需要填充B屬性,繼續第二步,發現B還沒有構造,於是開始B流程的構造過程,構造的時候發現需要填充A,從第三層緩存singletonFactories中找到A(此時的A還沒有完全構造完成,但是可以拿到A的一個引用),B拿到A的引用後,完成B自己的填充屬性工作,完成初始化工作,把自己放到第一層緩存singletonObjects中。這時回到A的這邊,在拿到B對象後,完成自己的填充屬性工作。

如果是構造器依賴屬性 會報循環依賴異常如果對象都是多例對象 會報循環依賴異常如果設置allowCircularReferences為false 會報循環依賴異常

protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) { if (this.allowBeanDefinitionOverriding != null) { beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } if (this.allowCircularReferences != null) { beanFactory.setAllowCircularReferences(this.allowCircularReferences); }}

擴展: bean的覆蓋問題

默認情況: 同一個配置文件中出現id相同的bean會報錯,不同的配置文件出現id相同的bean 後加載的bean會將先加載的bean覆蓋掉,稱為bean的覆蓋,bean的覆蓋不會報錯,但可能影響我們的項目 , 可以通過屬性設置 不允許bean的覆蓋allowBeanDefinitionOverriding設置為false

,
同类文章
葬禮的夢想

葬禮的夢想

夢見葬禮,我得到了這個夢想,五個要素的五個要素,水火只好,主要名字在外面,職業生涯良好,一切都應該對待他人治療誠意,由於小,吉利的冬天夢想,秋天的夢是不吉利的
找到手機是什麼意思?

找到手機是什麼意思?

找到手機是什麼意思?五次選舉的五個要素是兩名士兵的跡象。與他溝通很好。這是非常財富,它擅長運作,職業是仙人的標誌。單身男人有這個夢想,主要生活可以有人幫忙
我不怎麼想?

我不怎麼想?

我做了什麼意味著看到米飯烹飪?我得到了這個夢想,五線的主要土壤,但是Tu Ke水是錢的跡象,職業生涯更加真誠。他真誠地誠實。這是豐富的,這是夏瑞的巨星
夢想你的意思是什麼?

夢想你的意思是什麼?

你是什​​麼意思夢想的夢想?夢想,主要木材的五個要素,水的跡象,主營業務,主營業務,案子應該抓住魅力,不能疏忽,春天夢想的吉利夢想夏天的夢想不幸。詢問學者夢想
拯救夢想

拯救夢想

拯救夢想什麼意思?你夢想著拯救人嗎?拯救人們的夢想有一個現實,也有夢想的主觀想像力,請參閱週宮官方網站拯救人民夢想的詳細解釋。夢想著敵人被拯救出來
2022愛方向和生日是在[質量個性]中

2022愛方向和生日是在[質量個性]中

[救生員]有人說,在出生88天之前,胎兒已經知道哪天的出生,如何有優質的個性,將走在什麼樣的愛情之旅,將與生活生活有什么生活。今天
夢想切割剪裁

夢想切割剪裁

夢想切割剪裁什麼意思?你夢想切你的手是好的嗎?夢想切割手工切割手有一個真正的影響和反應,也有夢想的主觀想像力。請參閱官方網站夢想的細節,以削減手
夢想著親人死了

夢想著親人死了

夢想著親人死了什麼意思?你夢想夢想你的親人死嗎?夢想有一個現實的影響和反應,還有夢想的主觀想像力,請參閱夢想世界夢想死亡的親屬的詳細解釋
夢想搶劫

夢想搶劫

夢想搶劫什麼意思?你夢想搶劫嗎?夢想著搶劫有一個現實的影響和反應,也有夢想的主觀想像力,請參閱週恭吉夢官方網站的詳細解釋。夢想搶劫
夢想缺乏缺乏紊亂

夢想缺乏缺乏紊亂

夢想缺乏缺乏紊亂什麼意思?你夢想缺乏異常藥物嗎?夢想缺乏現實世界的影響和現實,還有夢想的主觀想像,請看官方網站的夢想組織缺乏異常藥物。我覺得有些東西缺失了