? ? ? ? 簡單的說,ioc--控制反轉(zhuǎn)溉潭,就是把對象生命周期的管理權(quán)限交給Spring去管理净响。不管是對象的創(chuàng)建還是對象的銷毀少欺,均由Spring去打理
? ? ? ? 對象的創(chuàng)建不再交給程序本身,對象的銷毀也不再是程序本身的責(zé)任馋贤。
我們現(xiàn)在拿一個(gè)小例子講一下赞别,Spring IOC的思想:
首先我們創(chuàng)建一根DAO接口:
public interface UserDao {
void getUser();
}
然后我們創(chuàng)建他的實(shí)現(xiàn)類:
public class UserDaoImplimplements UserDao {
@Override
? ? public void getUser() {
System.out.println("User");
? ? }
}
然后我們寫一個(gè)Service接口:
public interface UserSerevice? {
void getUser();
}
然后將Service接口的實(shí)現(xiàn)類寫出來:
public class UserServiceImplimplements UserSerevice {
private UserDaouserDao=new? UserDaoImplimplements ()? ;
? ? public void setUserDao(UserDao userDao) {
this.userDao = userDao;
? ? }
@Override
? ? public void getUser() {
userDao.getUser();
? ? }
}
我們寫一個(gè)入口程序去創(chuàng)建其對象:
public class Test {
public static void main(String[] args) {
UserServiceImpl service=new UserServiceImpl();
? ? ? ? service.getUser();
? ? }
}
輸出:
大家應(yīng)該可以看到,我在service接口的實(shí)現(xiàn)類里面是直接把Dao這個(gè)實(shí)現(xiàn)類直接在里面new出來了配乓,這樣子的耦合性很強(qiáng)仿滔,不利于后期的修改,所以扰付,我就修改成為這樣子:
然后我們可以創(chuàng)建兩個(gè)Dao實(shí)現(xiàn)類堤撵,但是里面的內(nèi)容你們可以自己修改,這個(gè)我就不一一寫出來了:
程序的入口程序可以這樣寫:
public class Test {
public static void main(String[] args) {
UserServiceImpl userDao =new UserServiceImpl();
? ? ? ? userDao.setUserDao(new UserDaoMySqlImpl());
? ? ? ? userDao.getUser();
? ? ? ? userDao.setUserDao(new UserDaoOracleImpl());
? ? ? ? userDao.getUser();
? ? }
}
然后我們看輸出:
? ? ? ? 我這樣寫羽莺,極大的降低了程序和接口之間的耦合性实昨,方便以后的程序修改,然后我在主程序里面可以分開創(chuàng)建對象盐固,這樣做也方便之后程序的管理荒给。我在setUserDao方法里面寫的是Dao的接口,通過轉(zhuǎn)型刁卜,替換為實(shí)現(xiàn)類志电,進(jìn)一步得到相應(yīng)的實(shí)現(xiàn)功能。
? ? 但是蛔趴,我們使用了Spring以后就不一樣了挑辆,對象的產(chǎn)生和銷毀都交給Spring去統(tǒng)一管理,不再由程序本身去管理孝情。
? ? 我們可以寫一個(gè)小示例試試看:
我們先創(chuàng)建一個(gè)Spring的項(xiàng)目鱼蝉,然后先寫一個(gè)bean類:public class User {
private Stringusername;
? ? private Stringpassword;
? ? public StringgetUsername() {
return username;
? ? }
public void setUsername(String username) {
this.username = username;
? ? }
public StringgetPassword() {
return password;
? ? }
public void setPassword(String password) {
this.password = password;
? ? }
public void UserShow() {
System.out.println("Username:" +username +"\n\n"+"password:" +password);
? ? }
}
然后我們寫一個(gè)xml配置文件:
然后我們寫一個(gè)實(shí)現(xiàn)方法:
public class Test {
public static void main(String[] args) {
ApplicationContext context=new ClassPathXmlApplicationContext("com/spring/demo/controller/user.xml");
? ? ? ? User user = (User) context.getBean("user");
? ? ? ? user.UserShow();
? ? }
}
注意,路徑問題箫荡,我寫的是我的包名加上文件名魁亦,因?yàn)槲野蓌ml文件放在我的同目錄寫,所有我這樣寫羔挡,如果你們是maven項(xiàng)目洁奈,你們把包名路徑換成classpath:就好了,如果是java項(xiàng)目绞灼,你們寫WEB-INF/+文件名就好了
然后我們點(diǎn)擊運(yùn)行利术,我們看看結(jié)果:
Look,我們是不是沒有創(chuàng)建對象啊低矮,但是你肯定會說印叁,是真的創(chuàng)建對象了嗎,那我們在bean類的構(gòu)造方法里面寫一個(gè)隊(duì)友的輸出:
點(diǎn)擊運(yùn)行:
你看,是不是創(chuàng)建對象了
? ? ? ? Spring就是這樣子喉钢,我們不需要再去按照老樣子自己在程序里面創(chuàng)建對象,銷毀對象良姆。一切的對象管理都交給Spring去做肠虽,告訴他,我什么時(shí)候需要創(chuàng)建對象了玛追,我什么時(shí)候要加入什么值税课。對象的管理權(quán)交給了Spring,我們不需要去管理痊剖。
? ? ? ? 簡單粗俗一點(diǎn)說韩玩,好比你爸爸的工資使用權(quán)不再是你爸爸的,這個(gè)使用權(quán)都給你老媽了陆馁,你爸爸沒有權(quán)利去管理自己的工資找颓。
然后我們現(xiàn)在總結(jié):
第一點(diǎn):什么是IOC:
IoC 容器:最主要是完成了完成對象的創(chuàng)建和依賴的管理注入等等。
先從我們自己設(shè)計(jì)這樣一個(gè)視角來考慮:
所謂控制反轉(zhuǎn)叮贩,就是把原先我們代碼里面需要實(shí)現(xiàn)的對象創(chuàng)建击狮、依賴的代碼,反轉(zhuǎn)給容器來幫忙實(shí)現(xiàn)益老。那么必然的我們需要創(chuàng)建一個(gè)容器彪蓬,同時(shí)需要一種描述來讓容器知道需要創(chuàng)建的對象與對象的關(guān)系。這個(gè)描述最具體表現(xiàn)就是我們可配置的文件捺萌。
對象和對象關(guān)系怎么表示档冬?
可以用 xml , properties 文件等語義化配置文件表示桃纯。
描述對象關(guān)系的文件存放在哪里酷誓?
可能是 classpath , filesystem 慈参,或者是 URL 網(wǎng)絡(luò)資源呛牲, servletContext 等。
回到正題驮配,有了配置文件娘扩,還需要對配置文件解析。
不同的配置文件對對象的描述不一樣壮锻,如標(biāo)準(zhǔn)的琐旁,自定義聲明式的,如何統(tǒng)一猜绣? 在內(nèi)部需要有一個(gè)統(tǒng)一的關(guān)于對象的定義灰殴,所有外部的描述都必須轉(zhuǎn)化成統(tǒng)一的描述定義。
如何對不同的配置文件進(jìn)行解析掰邢?需要對不同的配置文件語法牺陶,采用不同的解析器
第二點(diǎn): Spring IOC體系結(jié)構(gòu)
(1) BeanFactory
? ? ? ? Spring Bean的創(chuàng)建是典型的工廠模式伟阔,這一系列的Bean工廠,也即IOC容器為開發(fā)者管理對象間的依賴關(guān)系提供了很多便利和基礎(chǔ)服務(wù)掰伸,在Spring中有許多的IOC容器的實(shí)現(xiàn)供用戶選擇和使用皱炉,其相互關(guān)系如下:
? ? ? ? 其中BeanFactory作為最頂層的一個(gè)接口類,它定義了IOC容器的基本功能規(guī)范狮鸭,BeanFactory 有三個(gè)子類:ListableBeanFactory合搅、HierarchicalBeanFactory 和AutowireCapableBeanFactory。但是從上圖中我們可以發(fā)現(xiàn)最終的默認(rèn)實(shí)現(xiàn)類是 DefaultListableBeanFactory歧蕉,他實(shí)現(xiàn)了所有的接口灾部。那為何要定義這么多層次的接口呢?查閱這些接口的源碼和說明發(fā)現(xiàn)惯退,每個(gè)接口都有他使用的場合赌髓,它主要是為了區(qū)分在 Spring 內(nèi)部在操作過程中對象的傳遞和轉(zhuǎn)化過程中,對對象的數(shù)據(jù)訪問所做的限制蒸痹。例如 ListableBeanFactory 接口表示這些 Bean 是可列表的春弥,而 HierarchicalBeanFactory 表示的是這些 Bean 是有繼承關(guān)系的,也就是每個(gè)Bean 有可能有父 Bean叠荠。? ? ? ?
? ? ? ? ? ? AutowireCapableBeanFactory 接口定義 Bean 的自動裝配規(guī)則匿沛。這四個(gè)接口共同定義了 Bean 的集合、Bean 之間的關(guān)系榛鼎、以及 Bean 行為.
? ? ? ? 在BeanFactory里只對IOC容器的基本行為作了定義逃呼,根本不關(guān)心你的bean是如何定義怎樣加載的。正如我們只關(guān)心工廠里得到什么的產(chǎn)品對象者娱,至于工廠是怎么生產(chǎn)這些對象的抡笼,這個(gè)基本的接口不關(guān)心。
? ? ? ? ? ? 而要知道工廠是如何產(chǎn)生對象的黄鳍,我們需要看具體的IOC容器實(shí)現(xiàn)推姻,spring提供了許多IOC容器的實(shí)現(xiàn)。比如XmlBeanFactory框沟,ClasspathXmlApplicationContext等藏古。其中XmlBeanFactory就是針對最基本的ioc容器的實(shí)現(xiàn),這個(gè)IOC容器可以讀取XML文件定義的BeanDefinition(XML文件中對bean的描述),如果說XmlBeanFactory是容器中的屌絲忍燥,ApplicationContext應(yīng)該算容器中的高帥富.
? ? ? ? ? ? ApplicationContext是Spring提供的一個(gè)高級的IoC容器拧晕,它除了能夠提供IoC容器的基本功能外,還為用戶提供了以下的附加服務(wù)梅垄。
從ApplicationContext接口的實(shí)現(xiàn)厂捞,我們看出其特點(diǎn):
? ? ? ? 1.? 支持信息源,可以實(shí)現(xiàn)國際化。(實(shí)現(xiàn)MessageSource接口)
? ? ? ? 2.? 訪問資源靡馁。(實(shí)現(xiàn)ResourcePatternResolver接口欲鹏,這個(gè)后面要講)
? ? ? ? 3.? 支持應(yīng)用事件。(實(shí)現(xiàn)ApplicationEventPublisher接口)
(2) BeanDefinition
? ? ? ? SpringIOC容器管理了我們定義的各種Bean對象及其相互的關(guān)系臭墨,Bean對象在Spring實(shí)現(xiàn)中是以BeanDefinition來描述的貌虾,其繼承體系如下:
? ? ? ? Bean 的解析過程非常復(fù)雜,功能被分的很細(xì)裙犹,因?yàn)檫@里需要被擴(kuò)展的地方很多,必須保證有足夠的靈活性衔憨,以應(yīng)對可能的變化叶圃。Bean 的解析主要就是對 Spring 配置文件的解析。這個(gè)解析過程主要通過下圖中的類完成:
Spring IOC控制反轉(zhuǎn)践图,我就基本介紹到這里掺冠,個(gè)人見解,如果有什么不對的地方码党,請指正