用java的spring實(shí)現(xiàn)一個(gè)簡(jiǎn)單的IOC容器示例代碼
要想深入的理解IOC的技術(shù)原理,沒(méi)有什么能比的上我們自己實(shí)現(xiàn)它。這次我們一起實(shí)現(xiàn)一個(gè)簡(jiǎn)單IOC容器。讓大家更容易理解Spring IOC的基本原理。
這里會(huì)涉及到一些java反射的知識(shí),如果有不了解的,可以自己去找些資料看看。
注意
在上一篇文章,我說(shuō),啟動(dòng)IOC容器時(shí),Spring會(huì)將xml文件里面配置的bean掃描并實(shí)例化,其實(shí)這種說(shuō)法不太準(zhǔn)確,所以我在這里更正一下,xml文件里面配置的非單利模式的bean,會(huì)在第一次調(diào)用的時(shí)候被初始化,而不是啟動(dòng)容器的時(shí)候初始化。但是我們這次要做的例子是容器啟動(dòng)的時(shí)候就將bean初始化。特此說(shuō)明一下,害怕誤導(dǎo)初學(xué)者。
現(xiàn)在我們開(kāi)始做一個(gè)簡(jiǎn)單的IOC容器
思路:
1,啟動(dòng)容器時(shí),加載xml文件。
2,讀取xml文件內(nèi)的bean信息,并使用反射技術(shù)將bean實(shí)例化,并裝入容器。
3,確認(rèn)bean之間的以來(lái)關(guān)系,進(jìn)行注入。
下面直接上代碼,先看配置文件,與上一篇文章中使用的例子是一樣的,我們這次繼續(xù)使用上一篇文章的吃蘋(píng)果和吃橘子的例子,只不過(guò)這次我們用我們自己寫(xiě)的IOC容器,所以,我只粘貼了關(guān)鍵代碼。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING/DTD BEAN/EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <!--這是吃橘子的Bean --> <bean id="eatOrange" class="it.spring.liao.com.EatOrange"></bean> <!--這是吃蘋(píng)果的Bean --> <bean id="eatApple" class="it.spring.liao.com.EatApple"></bean> <bean id="person" class="it.spring.liao.com.Person"> <!-- 這里我們注入的是吃橘子的bean--> <property name="eat" ref="eatOrange"/> </bean> </beans>
此處為關(guān)鍵代碼
package it.spring.liao.com; import java.io.InputStream; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import org.dom4j.Attribute; import org.dom4j.Document; import org.dom4j.Element; import org.dom4j.io.SAXReader; public class BeanFactory { // 用于存放bean實(shí)例的集合 private Map<String, Object> beanMap = new HashMap<String, Object>(); /** * bean工廠的初始化. <br> * * @param xml * 配置文件路徑 */ public void init(String xml) { try { // 1.創(chuàng)建讀取配置文件的reader對(duì)象 SAXReader reader = new SAXReader(); // 2.獲取當(dāng)前線程中的類(lèi)裝載器對(duì)象 ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); // 3.從class目錄下獲取指定的xml文件 InputStream ins = classLoader.getResourceAsStream(xml); // 4.使用dom4j 解析xml文件 Document doc = reader.read(ins); Element root = doc.getRootElement(); // 5.初始化bean setBean(root); // 6.注入bean的依賴(lài)關(guān)系 setPv(root); } catch (Exception e) { System.out.println(e.toString()); } } /** * 初始化bean * * @param root * xml文件 * @throws Exception */ public void setBean(Element root) throws Exception { // 1.遍歷xml文件當(dāng)中的Bean實(shí)例 for (Iterator i = root.elementIterator("bean"); i.hasNext();) { Element foo = (Element) i.next(); // 2.針對(duì)每個(gè)Bean實(shí)例,獲取bean的屬性id和class String id = foo.attribute("id").getText(); String cls = foo.attribute("class").getText(); // 3.利用Java反射機(jī)制,通過(guò)class的名稱(chēng)獲取Class對(duì)象 Class bean = Class.forName(cls); // 4.創(chuàng)建對(duì)象 Object obj = bean.newInstance(); // 5.將對(duì)象放入beanMap中,其中key為bean的id值,value為bean的實(shí)例 beanMap.put(id, obj); } } /** * 注入bean的依賴(lài)關(guān)系 * * @param root * xml文件 * @throws Exception */ public void setPv(Element root) throws Exception { for (Iterator it = root.elementIterator("bean"); it.hasNext();) { Element foo = (Element) it.next(); // 1.針對(duì)每個(gè)Bean實(shí)例,獲取bean的屬性id和class String cls = foo.attribute("class").getText(); String id = foo.attribute("id").getText(); // 2.利用Java反射機(jī)制,通過(guò)class的名稱(chēng)獲取Class對(duì)象 Class bean1 = Class.forName(cls); // 3.獲取對(duì)應(yīng)class的信息 java.beans.BeanInfo info = java.beans.Introspector.getBeanInfo(bean1); // 4.獲取其屬性描述 java.beans.PropertyDescriptor pd[] = info.getPropertyDescriptors(); // 5遍歷該bean的property屬性 for (Iterator ite = foo.elementIterator("property"); ite.hasNext();) { Element foo2 = (Element) ite.next(); // 6.獲取該property的name屬性 String name = foo2.attribute("name").getText(); String ref = foo2.attribute("ref").getText(); // 7.在類(lèi)中尋找與xml配置文件中該bean的property屬性名相同的屬性 for (int k = 0; k < pd.length; k++) { // 8.如果相等,證明已經(jīng)找到對(duì)應(yīng)得屬性 if (pd[k].getName().equalsIgnoreCase(name)) { Method mSet = null; // 9.利用反射,獲取該屬性的set方法 mSet = pd[k].getWriteMethod(); // 10.用原beanMap中該bean的實(shí)例,執(zhí)行該屬性的set方法,并從原beanMap中獲取該屬性的依賴(lài)值 mSet.invoke(beanMap.get(id), beanMap.get(ref)); } } break; } } } /** * 通過(guò)bean的id獲取bean的實(shí)例 * * @param beanName * bean的id * @return 返回對(duì)應(yīng)對(duì)象 */ public Object getBean(String beanName) { Object obj = beanMap.get(beanName); return obj; } } /** * 測(cè)試方法. * * @param args */ public static void main(String[] args) { //使用我們自己寫(xiě)的 BeanFactory BeanFactory factory = new BeanFactory(); factory.init("eat.xml"); Person javaBean = (Person) factory.getBean("person"); System.out.println(javaBean.eat()); }
詳細(xì)的解釋都在代碼的注釋中,這個(gè)例子可以幫助你更深刻的理解spring的基本技術(shù)原理。但Spring的復(fù)雜程度遠(yuǎn)遠(yuǎn)高于這個(gè)例子,再說(shuō)一次,spring IOC中使用懶加載機(jī)制,在啟動(dòng)spring IOC時(shí),只會(huì)實(shí)例化單例模式的bean,不會(huì)實(shí)例化普通的bean,關(guān)于單例模式還是其他模式,是可以自己配置的,我們會(huì)在后面的文章中講解,非單例模式bean的實(shí)例化,發(fā)生在第一次調(diào)用的時(shí)候,與我們這個(gè)例子不太一樣。這個(gè)例子只供了解Spring IOC的基本原理,真實(shí)情況要復(fù)雜的多,需要我們一點(diǎn)點(diǎn)的去學(xué)習(xí),不積跬步無(wú)以至千里。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
SpringCloud Finchley+Spring Boot 2.0 集成Consul的方法示例(1.2版本)
這篇文章主要介紹了SpringCloud Finchley+Spring Boot 2.0 集成Consul的方法示例(1.2版本),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-08-08Java使用FutureTask實(shí)現(xiàn)預(yù)加載的示例詳解
基于FutureTask的特性,通??梢允褂肍utureTask做一些預(yù)加載工作,比如一些時(shí)間較長(zhǎng)的計(jì)算等,本文就來(lái)和大家講講具體實(shí)現(xiàn)方法吧,感興趣的可以了解一下2023-06-06關(guān)于BigDecimal類(lèi)型數(shù)據(jù)的絕對(duì)值和相除求百分比
這篇文章主要介紹了關(guān)于BigDecimal類(lèi)型數(shù)據(jù)的絕對(duì)值和相除求百分比,Java在java.math包中提供的API類(lèi)BigDecimal,用來(lái)對(duì)超過(guò)16位有效位的數(shù)進(jìn)行精確的運(yùn)算,需要的朋友可以參考下2023-07-07詳解servlet調(diào)用的幾種簡(jiǎn)單方式總結(jié)
這篇文章主要介紹了詳解servlet調(diào)用的幾種簡(jiǎn)單方式總結(jié),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01基于StringUtils工具類(lèi)的常用方法介紹(必看篇)
下面小編就為大家?guī)?lái)一篇基于StringUtils工具類(lèi)的常用方法介紹(必看篇)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-07-07取消idea雙擊shift鍵時(shí)出現(xiàn)的全局搜索的問(wèn)題分析
這篇文章主要介紹了取消idea雙擊shift鍵時(shí)出現(xiàn)的全局搜索的問(wèn)題分析,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2023-10-10單臺(tái)Spring Cloud Eureka升級(jí)到三臺(tái)Eureka高可用集群
今天小編就為大家分享一篇關(guān)于單臺(tái)Spring Cloud Eureka升級(jí)到三臺(tái)Eureka高可用集群,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2018-12-12