更新時間:2020-08-14 來源:黑馬程序員 瀏覽量:
面試官特別愛問SpringIOC底層實現(xiàn),Spring源碼晦澀難懂怎么辦呢? 跟著老師手動實現(xiàn)一個mini
ioc容器吧,實現(xiàn)后再回頭看Spring源碼事半功倍哦,就算直接和面試官講也完全可以哦,類名完全按照源碼設計,話不多說開干~!
需要實現(xiàn)的IOC功能:
·可以通過xml配置bean信息
·可以通過容器getBean獲取對象
·能夠根據(jù)Bean的依賴屬性實現(xiàn)依賴注入
·可以配置Bean的單例多例
實現(xiàn)簡易IOC設計的類
類/接口 | 說明 |
BeanFactory | IOC容器的基礎接口,提供IOC容器的基本功能 |
DefaultListableBeanFactory | IOC容器的核心實現(xiàn)類,提供多個map集合用來存儲bean的定義對象,提供getBean方法的核心實現(xiàn) |
XmlBeanFactory | IOC容器的實現(xiàn)類,基于xml構(gòu)建bean信息 |
XmlBeanDefinitionReader | 用于解析xml信息,并提供解析Document文檔的方法,并將解析到的BeanDefinition對象注冊到核心容器中 |
BeanDefinition | 封裝Bean的定義對象,如: bean的id class,scope ..等等 |
Property | 封裝Bean所關聯(lián)依賴的屬性 |
類之間關系模型
前期準備
創(chuàng)建maven項目引入依賴
<dependencies> <!-- 解析xml --> <dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifactId> <version>1.1</version> </dependency> <!-- BeanUtils --> <dependency> <groupId>commons-beanutils</groupId> <artifactId>commons-beanutils</artifactId> <version>1.9.3</version> </dependency> </dependencies>
準備3個bean的實體類
/** * 學生類 * 學生類依賴班級對象 * 并提供 sayHello() 方法 * @作者 itcast * @創(chuàng)建日期 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 * @創(chuàng)建日期 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 * @創(chuàng)建日期 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班
依賴教師對象: 陳老師
<?xml version="1.0" encoding="UTF-8"?> <beans> <!-- 配置IOC容器要管理的對象 bean作用域: 單例 原型 --> <bean id="student" class="com.itcast.ioc.bean.Student" scope="singleton" lazy-init="true"> <!-- 依賴注入: 屬性注入 構(gòu)造器注入 注解注入--> <property name="name" value="小明"></property> <property name="tClass" ref="tclass"></property> </bean> <bean id="tclass" class="com.itcast.ioc.bean.TClass"> <property name="cname" value="3年2班"></property> <property name="teacher" ref="teacher"></property> </bean> <bean id="teacher" class="com.itcast.ioc.bean.Teacher"> <property name="tname" value="陳老師"></property> </bean> </beans>
定義BeanFactory
/** * 容器的基礎接口 * 提供容器最基本的功能 */ public interface BeanFactory { // 核心方法 獲取對象 Object getBean(String beanName); }
定義DefaultListableBeanFactory
package com.itcast.ioc.core; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * 基礎容器的核心實現(xiàn) * 提供 beanDefinitionMap 存儲bean的定義 * 提供 singletonObjects 存儲bean的對象實例 * @作者 itcast * @創(chuàng)建日期 2020/7/8 15:37 **/ public class DefaultListableBeanFactory implements BeanFactory { // 提供 beanDefinitionMap 存儲bean的定義 private Map<String,BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(); // 提供 singletonObjects 存儲bean的對象實例 (scope為singleton的) private Map<String,Object> singletonObjects = new ConcurrentHashMap<>(); /** * 實現(xiàn)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 * @創(chuàng)建日期 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<Property> 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<Property> getPropertyList() { return propertyList; } public void setPropertyList(List<Property> propertyList) { this.propertyList = propertyList; } }
定義Property
/** * 用于封裝一個property標簽 * 屬性數(shù)據(jù) * @作者 itcast * @創(chuàng)建日期 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
/** * 繼承核心實現(xiàn)類 * 基于xml配置bean的實現(xiàn)類 * @作者 itcast * @創(chuàng)建日期 2020/7/8 15:47 **/ public class XmlBeanFactory extends DefaultListableBeanFactory { /** * 將解析配置文件 注冊bean的所有工作交給reader對象 */ final XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(this); /** * 構(gòu)造器需要傳入xml配置文件 * @param configPath */ public XmlBeanFactory(String configPath) { // 使用reader對象 解析配置 注冊Bean this.xmlBeanDefinitionReader.loadBeanDefinitions(configPath); } }
定義XmlBeanDefinitionReader
/** * 解析配置 * 注冊到容器中 * @作者 itcast * @創(chuàng)建日期 2020/7/8 15:51 **/ public class XmlBeanDefinitionReader { // 核心beanfactory對象 用于將解析后的bean注冊到beanfactory中 final DefaultListableBeanFactory beanFactory; public XmlBeanDefinitionReader(DefaultListableBeanFactory beanFactory) { this.beanFactory = beanFactory; } /** * 根據(jù)傳遞的配置文件 * 解析配置 * 注冊bean * @param configPath */ void loadBeanDefinitions(String configPath){ } }
實現(xiàn)步驟
1. 通過dom4j解析xml得到Document文檔
2. 遍歷文檔所有Bean標簽
3. 解析每一個Bean標簽 封裝一個BeanDefinition對象
4. 解析每一個Bean標簽下的所有Property標簽 封裝一個Property對象
5. 將BeanDefinition和Property對象注冊到容器
實現(xiàn)xml解析及bean注冊
/** * 解析配置 * 注冊到容器中 * @作者 itcast * @創(chuàng)建日期 2020/7/8 15:51 **/ public class XmlBeanDefinitionReader { // 核心beanfactory對象 用于將解析后的bean注冊到beanfactory中 final DefaultListableBeanFactory beanFactory; public XmlBeanDefinitionReader(DefaultListableBeanFactory beanFactory) { this.beanFactory = beanFactory; } /** * 根據(jù)傳遞的配置文件 * 解析配置 * 注冊bean * @param configPath */ void loadBeanDefinitions(String configPath){ // 1. 通過dom4j解析xml得到Document文檔 Document document = doLoadDocument(configPath); // 2. 遍歷文檔所有Bean標簽 Element rootElement = document.getRootElement(); List<Element> 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<Element> 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出現(xiàn)異常==>"+e.getMessage()); throw new RuntimeException(e.getMessage()); } } }
準備測試類
/** * 測試類 * @作者 itcast * @創(chuàng)建日期 2020/7/8 16:19 **/ public class IocTest { public static void main(String[] args) { // 創(chuàng)建IOC容器 BeanFactory beanFactory = new XmlBeanFactory("applicationContext.xml"); // 通過容器獲取對象 Student student = (Student)beanFactory.getBean("student"); // 調(diào)用對象sayHello方法 student.sayHello(); } }
斷點查看注冊情況
可以看到我們配置的xml內(nèi)容 已經(jīng)解析成了BeanDefinition對象,注冊到了核心容器的map中
實現(xiàn)步驟
1. 先從單例的map集合中獲取 是否有指定beanName的對象
·有直接返回
·沒有下一步
2. 從注冊集合中獲取bean的定義對象
·有下一步
·沒有拋異常NoSuchBeanDefinition
3. 判斷bean的scope作用域
singleton單例
· createBean對象
·存入單例集合
·返回對象
prototype多例
·createBean對象
·返回對象
4. createBean方法
獲取BeanDefinition中的className
通過反射API得到Class對象
通過反射API得到bean實例
獲取BeanDefinition中依賴的屬性列表
實現(xiàn)屬性的依賴注入
實現(xiàn)getBean及createBean方法
/** * 基礎容器的核心實現(xiàn) * 提供 beanDefinitionMap 存儲bean的定義 * 提供 singletonObjects 存儲bean的對象實例 * @作者 itcast * @創(chuàng)建日期 2020/7/8 15:37 **/ public class DefaultListableBeanFactory implements BeanFactory { // 提供 beanDefinitionMap 存儲bean的定義 private Map<String,BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(); // 提供 singletonObjects 存儲bean的對象實例 (scope為singleton的) private Map<String,Object> singletonObjects = new ConcurrentHashMap<>(); /** * 實現(xiàn)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中依賴的屬性列表 * //實現(xiàn)屬性的依賴注入 * 創(chuàng)建對象 * @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()); } // 創(chuàng)建對象: Object obj; try { obj = aClass.newInstance(); } catch (InstantiationException e) { e.printStackTrace(); throw new RuntimeException("創(chuàng)建對象失敗"+e.getMessage()); } catch (IllegalAccessException e) { e.printStackTrace(); throw new RuntimeException("非法訪問"+e.getMessage()); } // 依賴注入 List<Property> 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<String,String> 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("調(diào)用目標對象失敗"+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("調(diào)用目標對象失敗"+e.getMessage()); } } } } return obj; } /** * 將bean注冊到容器中 * @param beanDefinition */ public void registerBeanDefinition(BeanDefinition beanDefinition){ beanDefinitionMap.put(beanDefinition.getBeanName(),beanDefinition); } }
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(); }
測試對象能否獲取
查看bean的注冊及單例集合信息
可以通過變更scope的值查看對應的變化
IOC容器源碼及其它面試細節(jié)
擴展: 容器如何創(chuàng)建對象
IOC容器在準備創(chuàng)建對象時, 會判斷是否有配置 factory-method方法
如果有配置 會調(diào)用factory-method所指向的方法構(gòu)建對象.
如果沒配置,會檢查是否有配置構(gòu)造參數(shù)
無構(gòu)造參數(shù): 調(diào)用默認構(gòu)造器創(chuàng)建對象
有構(gòu)造參數(shù): 根據(jù)參數(shù)情況匹配對應的構(gòu)造器
擴展: bean的生命周期
spring 容器中的bean的完整生命周期一共分為十一步完成。
1.bean對象的實例化
2.封裝屬性,也就是設置properties中的屬性值
3.如果bean實現(xiàn)了BeanNameAware,則執(zhí)行setBeanName方法,也就是bean中的id值
4.如果實現(xiàn)BeanFactoryAware或者ApplicationContextAware ,需要設置setBeanFactory或者上下文對象setApplicationContext
5.如果存在類實現(xiàn)BeanPostProcessor后處理bean,執(zhí)行postProcessBeforeInitialization,可以在初始化之前執(zhí)行一些方法
6.如果bean實現(xiàn)了InitializingBean,則執(zhí)行afterPropertiesSet,執(zhí)行屬性設置之后的操作
7.調(diào)用執(zhí)行指定的初始化方法
8.如果存在類實現(xiàn)BeanPostProcessor則執(zhí)行postProcessAfterInitialization,執(zhí)行初始化之后的操作
9.執(zhí)行自身的業(yè)務方法
10.如果bean實現(xiàn)了DisposableBean,則執(zhí)行spring的的銷毀方法
11.調(diào)用執(zhí)行自定義的銷毀方法。
擴展: bean的循環(huán)依賴問題
A 依賴 B B 依賴 A 產(chǎn)生閉環(huán),稱為循環(huán)依賴
·Spring 默認允許單例對象的屬性注入 所產(chǎn)生的循環(huán)依賴
單例對象的循環(huán)依賴 Spring通過3級緩存來解決
比如一個類A中有一個屬性是B類,B類中有一個屬性是A類,這時看Spring是怎么解決他們的相互依賴的。Spring注入一個類的大體步驟分為兩部分,一是先完成對類的構(gòu)造工作,二是會對類的屬性進行設置和填充。首先Spring構(gòu)造A類,通過AbstractAutowireCapableBeanFactory的doCreateBean方法中調(diào)用addSingletonFactory方法將A類曝光到singletonFactories中。這時完成A的構(gòu)造后,需要填充B屬性,繼續(xù)第二步,發(fā)現(xiàn)B還沒有構(gòu)造,于是開始B流程的構(gòu)造過程,構(gòu)造的時候發(fā)現(xiàn)需要填充A,從第三層緩存singletonFactories中找到A(此時的A還沒有完全構(gòu)造完成,但是可以拿到A的一個引用),B拿到A的引用后,完成B自己的填充屬性工作,完成初始化工作,把自己放到第一層緩存singletonObjects中。這時回到A的這邊,在拿到B對象后,完成自己的填充屬性工作。
源碼 | 級別 | 描述 |
singletonObjects | 一級緩存 | 用于存放完全初始化好的 bean,從該緩存中取出的 bean 可以直接使用 |
earlySingletonObjects | 二級緩存 | 存放原始的 bean 對象(尚未填充屬性),用于解決循環(huán)依賴 |
singletonFactories | 三級緩存 | 存放 bean 工廠對象,用于解決循環(huán)依賴 |
·如果是構(gòu)造器依賴屬性 會報循環(huán)依賴異常
·如果對象都是多例對象 會報循環(huán)依賴異常
·如果設置allowCircularReferences為false 會報循環(huán)依賴異常
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) { if (this.allowBeanDefinitionOverriding != null) { beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } if (this.allowCircularReferences != null) { beanFactory.setAllowCircularReferences(this.allowCircularReferences); } }
擴展: bean的覆蓋問題
默認情況:
同一個配置文件中出現(xiàn)id相同的bean會報錯,不同的配置文件出現(xiàn)id相同的bean后加,載的bean會將先加載的bean覆蓋掉稱為bean的覆蓋,bean的覆蓋不會報錯,但可能影響我們的項目,可以通過屬性設置不允許bean的覆蓋,allowBeanDefinitionOverriding設置為false。
猜你喜歡: