Spring的Ioc模擬實(shí)現(xiàn)詳細(xì)介紹
簡(jiǎn)單來說就是當(dāng)自己需要一個(gè)對(duì)象的時(shí)候不需要自己手動(dòng)去new一個(gè),而是由其他容器來幫你提供;Spring里面就是IOC容器。
例如:
在Spring里面經(jīng)常需要在Service這個(gè)裝配一個(gè)Dao,一般是使用@Autowired注解:類似如下
public Class ServiceImpl{ @Autowired Dao dao; public void getData(){ dao.getData(); }
在這里未初始化Dao直接使用是會(huì)報(bào)出空指針異常的,那么在Spring里面的做法就是通過反射來將需要的類幫你加載進(jìn)來。
關(guān)于IOC:我們講個(gè)故事吧!
有一個(gè)廚師,他在做一道菜的時(shí)候需要某種調(diào)味料(bean),可是他正好沒有那瓶調(diào)味料(bean),這個(gè)時(shí)候他就必須去制作一瓶調(diào)味料(bean)出來。(這就像我們平時(shí)需要對(duì)象的時(shí)候一樣:UserDao userdao=new UserDaoImpl)這個(gè)時(shí)候廚師的工作就不得不跟制作調(diào)味料聯(lián)系起來,這就是所謂的“耦合”。(耦合程度高通說來說就是,我沒了你我活不了了。);
這個(gè)時(shí)候IOC里的控制反轉(zhuǎn)出來了,它提出,開辟一個(gè)倉庫(容器),里面放著各種各樣的調(diào)味料(bean),當(dāng)你需要某種調(diào)味料(bean)的時(shí)候只要給出調(diào)味料的名字(beanName)就可以直接去拿取,代碼類似這樣:(UserDao user=倉庫.get("調(diào)味料名字"));這樣你就可以直接拿到了調(diào)味料 (bean)而不用等到你沒有的時(shí)候再去制作一瓶出來。這就是所謂的將控制權(quán)轉(zhuǎn)移出來,它將生產(chǎn)調(diào)味料(bean)的工作直接讓倉庫(容器)來制作(生產(chǎn));
接下來依賴注入出來了,它就更加地強(qiáng)大的了,它提出廚師只要有一個(gè)瓶子(private UserDao userdao),并且將瓶子蓋打開(方法:setUserDao( UserDao userdao){this.userdao=userdao}即提供對(duì)于的set方法 )再給瓶子貼上一個(gè)標(biāo)簽注明要放什么材料(在xml文件中配置一下<property name="userdao",ref="xxbean"),那么這個(gè)時(shí)候你什么都不用做了,倉庫(容器)會(huì)自動(dòng)派人幫你將你所需的調(diào)味料(bean)放入到瓶子中 ;當(dāng)你想換另一種調(diào)味料的時(shí)候只要將瓶子上的標(biāo)簽改成其他比如胡椒粉(這時(shí)要在xml文件中更改一下ref的bean就行了),依賴注入提高了代碼的靈活性,你需要替換類的實(shí)現(xiàn)的時(shí)候,不需要去修改只要在xml配置文件里修改一下就行了;
總結(jié)一下我的總結(jié):IOC的主要目的就是實(shí)現(xiàn)解耦!解耦!解耦!重要的事情說三遍.由廚師的列子來說就是.我是一個(gè)廚師我只做菜,我不做調(diào)味料~~我不是一個(gè)做調(diào)味料的,我不跟制作調(diào)味料有那么大的關(guān)系. IOC的出現(xiàn),廚師只要安心做好自己的事情(做菜),需要調(diào)味料直接去倉庫拿取就行了!那么這樣廚師跟調(diào)味料的制作之間的關(guān)系就分離開來了,這就是解耦!解耦!解耦!使兩個(gè)東西的關(guān)系沒那么緊密.
模擬IOC的實(shí)現(xiàn),主要使用的技術(shù)是java的反射機(jī)制(模擬使用的是架構(gòu)分為dao層,service層,controller層):
注:歡迎各位大佬的指點(diǎn),小弟也在學(xué)習(xí),說得不好多多包涵~
一.編寫dao類,用于測(cè)試:
public interface IocDao { public void sayhello(); //一個(gè)用來測(cè)試的接口 }
二.編寫dao的實(shí)現(xiàn)類:
public class IocDaoImpl implements IocDao { @Override public void sayhello() { // TODO Auto-generated method stub System.out.println("hello word");//實(shí)現(xiàn)我們的dao接口里的方法 } }
三.編寫service類:
public interface IocDaoService { public void sayhello();//業(yè)務(wù)類接口,這里就跟dao一樣 }
四.編寫service的實(shí)現(xiàn)類:
public class IocDaoServiceImpl implements IocDaoService { private IocDao iocDao; //創(chuàng)建一個(gè)接口. public IocDao getIocDao() { return iocDao; } //編寫IocDao接口對(duì)應(yīng)的set方法用于依賴注入 //依賴注入的方式有三種:接口注入,構(gòu)造方法注入,set注入; //此處為set注入 public void setIocDao(IocDao iocDao) { this.iocDao = iocDao; } @Override public void sayhello() { // TODO Auto-generated method stub iocDao.sayhello();//調(diào)用接口方法 } }
五.編寫bean.xml配置文件.(這里的i你可以看成是IocDaoImpl類,iocService看成IocDaoServiceImpl,iocDao這是IocDaoServiceImpl里的一個(gè)屬性,這個(gè)屬性傳入的參數(shù)值為“i”);
<beans> <bean id="i" class="com.hck.dao.impl.IocDaoImpl"/> <bean id="iocService" class="com.hck.service.impl.IocDaoServiceImpl"> <property name="iocDao" ref="i"></property> </bean> </beans>
六.編寫工廠接口.
//模擬ClassPathXmlApplicationContext實(shí)現(xiàn)的一個(gè)接口BeanFactory public interface BeanFactory { public Object getBean(String beanName); }
七.編寫ClassPathXmlApplicationContext去讀取配置文件,并且根據(jù)bean.xml里的配置對(duì)象去生成各種bean,完成其中的注入工作.(最重要的部分Ioc的實(shí)現(xiàn)原理),一字一句都有注釋
//模擬ClassPathXmlApplicationContext去讀取配置文件 public class ClassPathXmlApplicationContext implements BeanFactory { //定義map集合來存放bean.xml里的bean的id跟其對(duì)應(yīng)的實(shí)例化對(duì)象 //<bean id="i" class="com.hck.dao.impl.IocDaoImpl"/> //那么類似的存放bean.put("i",new IocDaoImpl());這樣子. Map<String, Object> beans=new HashMap<String,Object>(); public ClassPathXmlApplicationContext(String xmlPath){ try { //創(chuàng)建SAXBuilder對(duì)象解析文檔 SAXBuilder saxBuilder = new SAXBuilder(); //解析build里的參數(shù)是一個(gè)文件路徑. Document document = saxBuilder.build(xmlPath); //document.getRootElement().getChildren("bean")獲取所有<bean>標(biāo)簽內(nèi)容 List elements = document.getRootElement().getChildren("bean"); //遍歷<bean>對(duì)象 for (int i = 0; i < elements.size(); i++) { //獲取第一個(gè)<bean>標(biāo)簽elements.get(0); Element element = (Element) elements.get(i); //獲取<bean>標(biāo)簽里的<id>屬性, //<bean id="i" class="com.hck.dao.impl.IocDaoImpl"/> //即String beanName="i"; String beanName = element.getAttributeValue("id"); //同上String clazz="com.hck.dao.impl.IocDaoImpl"; String clazz = element.getAttributeValue("class"); //加載類對(duì)象并且實(shí)例化.Object object=new IocDaoImpl(); Object object = Class.forName(clazz).newInstance();//object是IocDaoServiceImpl //將他們添加在map集合里,后面可以根據(jù)beanName直接獲取到實(shí)例化對(duì)象. beans.put(beanName, object); //遍歷<bean>標(biāo)簽下的<property>字標(biāo)簽. //第一個(gè)標(biāo)簽沒有字標(biāo)簽所以直接跳過.已第二個(gè)為例子 //<bean id="iocService" class="com.hck.service.impl.IocDaoServiceImpl"> //<property name="iocDao" ref="i"></property></bean> List elements2 = element.getChildren("property"); for (int j = 0; j < elements2.size(); j++) { //此處我們將獲得<property name="iocDao" ref="i"></property></bean> Element element2 = (Element) elements2.get(j); //相當(dāng)于String propertyName="iocDao"; String propertyName = element2.getAttributeValue("name"); //相當(dāng)于String refBean="i"; String refBean = element2.getAttributeValue("ref"); //相當(dāng)于String propertyName="IocDao"; //目的是為了得到一個(gè)方法的名字setIocDao,用于反射調(diào)用 propertyName = propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1); //這里的methodName="setIocDao"; String methodName = "set" + propertyName; //獲取Map集合里Key="i"的值;i對(duì)應(yīng)的是IocDaoImpl的實(shí)例化對(duì)象 //相當(dāng)于 Object object2 =IocDaoImpl; Object object2 = beans.get(refBean); //獲取IocDaoServiceImpl方法里的setIocDao方法. //第一個(gè)方法是方法名,第二個(gè)參數(shù)是方法的參數(shù)類型. Method method = object.getClass().getDeclaredMethod(methodName, object2.getClass().getInterfaces()); //調(diào)用方法,并傳入?yún)?shù),完成依賴注入. method.invoke(object, object2); } } // String beanName=document.getElementById(id).attributes().get("class"); // Object object=Class.forName(beanName).newInstance(); // return object; } catch (Exception e) { e.printStackTrace(); // TODO: handle exception } } /* (non-Javadoc) * @see com.hck.ioc.BeanFactory#getBean() */ @Override public Object getBean(String beanName) { // TODO Auto-generated method stub return beans.get(beanName); } }
八.編寫測(cè)試類
public class Ioc { public static void main(String[] args) { ClassPathXmlApplicationContext applicationContext=new ClassPathXmlApplicationContext("src/bean.xml"); IocDaoService ids=(IocDaoService)applicationContext.getBean("iocService"); ids.sayhello(); } }
九.顯示結(jié)果:
hello word
總結(jié)
以上就是本文關(guān)于Spring的Ioc模擬實(shí)現(xiàn)詳細(xì)介紹的全部?jī)?nèi)容,希望對(duì)大家有所幫助。歡迎參閱:spring的IoC和DI詳解、spring配置掃描多個(gè)包問題解析、SpringJDBC批量處理數(shù)據(jù)代碼示例等,有什么問題可以隨時(shí)留言,歡迎大家交流討論。
相關(guān)文章
java中實(shí)體類轉(zhuǎn)Json的2種方法
本篇文章中主要介紹了java中實(shí)體類轉(zhuǎn)Json的2種方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧。2017-01-01javaweb項(xiàng)目如何實(shí)現(xiàn)手機(jī)短信登錄
這篇文章主要介紹了javaweb項(xiàng)目如何實(shí)現(xiàn)手機(jī)短信登錄,手機(jī)號(hào)登錄在現(xiàn)在的項(xiàng)目中用的場(chǎng)景非常多,實(shí)現(xiàn)起來也不難,今天我們就一起來通過演示實(shí)現(xiàn)登錄過程,需要的朋友可以參考下2019-07-07idea雙擊圖標(biāo)打不開,無反應(yīng)的解決
這篇文章主要介紹了idea雙擊圖標(biāo)打不開,無反應(yīng)的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-09-09Java基于正則表達(dá)式獲取指定HTML標(biāo)簽指定屬性值的方法
這篇文章主要介紹了Java基于正則表達(dá)式獲取指定HTML標(biāo)簽指定屬性值的方法,涉及java基于正則的HTML元素匹配相關(guān)操作技巧,需要的朋友可以參考下2017-01-01add方法理解ArrayList的擴(kuò)容機(jī)制
這篇文章主要為大家介紹了add方法理解ArrayList的擴(kuò)容機(jī)制示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03Java實(shí)現(xiàn)SHA-256加密算法的完全解析
SHA-256是一種散列(哈希)算法,用于將任意長(zhǎng)度的數(shù)據(jù)映射為固定長(zhǎng)度的散列值,以保證數(shù)據(jù)完整性。本文將為大家介紹一下SHA-256加密算法的原理與實(shí)現(xiàn),希望對(duì)大家有所幫助2023-02-02Springboot PostMapping無法獲取數(shù)據(jù)問題及解決
這篇文章主要介紹了Springboot PostMapping無法獲取數(shù)據(jù)問題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-05-05