詳解SpringIOC容器相關知識

一、前言

IOC控制反轉,不是一種技術,而是一種設計思想,就是將原本在程序中手動創建對象的控制權,交給Spring框架來管理。

區別:

  • 沒有IOC的思路:若要使用某個對象,就必須自己負責去寫對象的創建
  • IOC的思路:若要使用某個對象,隻需要從Spring容器中獲取需要使用的對象,不關心對象的創建過程,也就是把創建對象的控制權交給瞭Spring框架。
  • 好萊塢法則:Don’t call me, I ‘ll call you

舉例說明:

做菜,做蒜薹炒豬肉

你有兩種做法:

第一種,自己養豬,然後種蒜薹。等到豬長大瞭,你就可以殺豬,蒜薹成熟瞭,就收割。然後開始炒,做成瞭蒜薹炒豬肉。

第二種,從農貿市場獲取豬和蒜薹,拿回來直接炒,做成瞭蒜薹炒豬肉。

此時的IOC就相當於這個農貿市場,我要做菜,我去農貿市場拿過來就可以瞭,而不需要自己去弄。為什麼要Java對象放到容器裡?因為我們要做到拿來即用,便於管理。那你能管理農貿市場嗎?你不能,那誰來管農貿市場?Spring!這就是控制反轉IOC,我們把控制權交給瞭Spring框架,他來幫我們管這個農貿市場,他來養豬,他來種菜。我們隻需在要菜的時候,去市場買就好瞭。

再舉一個例子

過年瞭,想要給傢裡打掃個衛生,你想請幾個鐘點工來打掃。也有兩種做法。

第一種:自己主動找,找身邊人看看誰認識鐘點工,你自己打電話邀約,談價格

第二種:直接找傢政公司,直接提出需求即可。

第一種方式就是我們自己創建對象的方式,自己主動new幾個鐘點工。而第二種就是spring給我們提供的IOC方式,傢政公司就是一個容器,能給我提供很多的服務,鐘點工對象是spring幫我們創建的。

又過瞭幾天,我又想給廚房的油煙機清理一下,也能直接打電話給傢政公司,提出需求。

那上述例子中的農貿市場和傢政公司哪裡來啊?

我們可以自己構建,就像自己成立一個公司一樣。具體在程序中表現為:

1.使用配置文件或者註解的方式定義一下我們自己容器裡存放的東西。

或者去別人的公司裡找。具體在程序中表現為:

2.一定有很多人創建瞭自己的公司,這些服務都可以集成在我們自己的容器裡,為我們提供強大的功能,比如spring自帶很多的template模板類。

二、IOC原理實戰

首先在pom.xml文件中加入spring的相關jar包。

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.2.0.RELEASE</version>
    </dependency>
</dependencies>

我們定義我們的接口和實現類

// UserDao接口
public interface UserDao {
    void getUser();
}
// UserDao實現類1,mysql實現
public class UserDaoImpl implements UserDao {
    public void getUser() {
        System.out.println("mysql實現");
    }
}
// UserDao實現類2,oracle實現
public class UserDaoImpl implements UserDao {
    public void getUser() {
        System.out.println("oracle實現");
    }
}

然後我們的業務實現類,在不使用set註入的情況下,是這樣的:

//業務接口
public interface UserService {
    void getUser();
}
//業務實現類
public class UserServiceImpl implements UserService {
    //傳統的方法中,如果這邊要改變,那就必須將這裡的語句改變才可以
    private UserDao userDao = new UserDaoImpl();

    public void getUser() {
        userDao.getUser();
    }
}

對應的測試類:

public class MyTest {
    public static void main(String[] args) {
        //用戶實際調用的是業務層,不需要接觸dao層
        UserServiceImpl userService =new UserServiceImpl();
        userService.getUser();
    }
}

但是你會發現使用這種方法如果我在測試這裡想用oracle實現,那就必須新增一個業務實現類或者修改我原本的業務實現類,違反瞭開閉原則。

所以我們的業務實現類要使用set方法動態註入我們的UserDao實現類。

public class UserServiceImpl implements UserService {
    private UserDao userDao;
    // 利用set進行動態實現值的註入
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public void getUser() {
        userDao.getUser();
    }
}

如此一來隻需要在測試類中通過set方法,傳入對應的實現類對象,就可以實現調用不同的實現對象的getUser方法。

public class MyTest {
    public static void main(String[] args) {
        // 利用set註入的方法,我們可以不需要修改service中的代碼,從而實現多個不同對象的getUser方法
        UserServiceImpl userService = new UserServiceImpl();
        userService.setUserDao(new UserDaoImpl());
        userService.getUser();//mysql實現
        userService.setUserDao(new UserDaoOracleImpl());
        userService.getUser();//oracle實現
    }
}

這兩種模式的區別可以發現。之前,控制UserDao實現類的控制權,在程序員手上,程序員寫在UserServiceImpl裡,寫死瞭對應的是實現類,如果要修改的話,程序員就必須去修改對應的代碼。而後面這種方法,控制UserDao實現類的控制權,就已經不在程序員手上瞭。現在程序是被動接收對象,然後動態set註入實現瞭可以隨意使用不同的實現類的getUser方法。

這其實就是一種控制反轉IOC的原型。這種思想從本質上解決瞭問題,程序員不用再去管理對象的創建瞭。系統的耦合性大大降低。可以更加專註的在業務的實現上。spring的底層全部都是基於這種思想去實現的。

三、IOC本質

在這裡插入圖片描述

像上圖所示,IOC本質上就是把左邊變成瞭右邊。本來是業務層裡程序員寫來主動決定調用的下面的Mysql還是Oracle,但是現在通過IOC,可以把主動權交給用戶,讓用戶想用Mysql用Mysql,想用Oracle就用Oracle。

DI(依賴註入)是實現IOC的一種方法,在沒有IOC的程序中,我們使用面向對象編程,對象的創建與對象間的依賴關系完全硬編碼再程序中,對象的創建由程序自己控制(也就是程序員自己寫),控制反轉(IOC)後將對象的創建移交給第三方瞭,控制反轉的這個反轉說的就是獲得依賴對象的方式反轉瞭。

采用XML配置方式配置Bean的時候,Bean的定義信息和實現是分離的,而采用註解的方式的時候兩者是合為一體的,Bean的定義信息直接以註解的形式定義在實現類中,從而達到瞭零配置的目睹。

控制反轉是一種通過描述(XML或者註解)並通過第三方去生產或獲得特定對象的方式。在Spring中實現控制反轉的是IOC容器,其實現方式是依賴註入(Dependency Injection,DI)

四、spring helloworld

找到1.2.2實例化容器部分,發現瞭其配置文件格式:

在這裡插入圖片描述

首先創建我們的實體類Hello:

package com.hj.pojo;
public class Hello {
    private String str;

    public String getStr() {
        return str;
    }

    public void setStr(String str) {
        this.str = str;
    }

    @Override
    public String toString() {
        return "Hello{" +
                "str='" + str + '\'' +
                '}';
    }
}

然後根據文檔中所述,在resources文件下創建beans.xml文件來使用spring創建對象。beans.xml內容如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--使用spring來創建對象,在spring中這些都稱為bean
    bean = 對象 相當於 new Hello();
    正常是 類型 變量名 = new 類型();
          Hello hello = new Hello();
    利用bean來實現,id就是變量名,class就是我們對象的類型
    裡面的property相當於給對象中的屬性設置一個值。
    -->
    <bean id="hello" class="com.hj.pojo.Hello">
        <!--
        ref:引用spring容器中創建好的對象
        value:具體的值,基本數據類型
        -->
        <property name="str" value="Spring"/>
    </bean>

</beans>

再次查看官方文檔,查詢如何使用容器。

在這裡插入圖片描述

可以看到需要借助一個工廠來讀取bean的定義並進行訪問,然後創建對象。

import com.hj.pojo.Hello;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {    
	public static void main(String[] args) {        
		//獲取spring的上下文對象        
		ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");        
		//我們的對象現在都在spring中管理瞭,我們要使用,直接去取出來就可以瞭        
		Hello hello = (Hello) context.getBean("hello");        
		System.out.println(hello.toString());//Hello{str='Spring'}        
		//思考?        
		//Hello對象是誰創建的?是由Spring創建的        
		//Hello對象的屬性是怎麼設置的?是由Spring容器設置的    
		}
	}

這個Hello對象由spring創建並且由spring容器設置屬性的過程就是控制反轉。

五、小結

控制:誰來控制對象的創建,傳統的應用程序的對象是由程序本身控制創建的,使用spring後,對象是由spring來創建的。

反轉:程序本身不創建對象,而變成被動的接收對象

依賴註入:就是利用set方法來進行註入

IOC是一種編程思想,由主動的編程去變成被動的接收。

我們回頭看Hello類裡左邊有個豆子的標志瞭,這說明這個類已經被Spring托管瞭。

在這裡插入圖片描述

所謂的IoC,一句話來概括:對象由spring來創建,管理和裝配。

到此這篇關於詳解SpringIOC和容器相關知識的文章就介紹到這瞭,更多相關SpringIOC和容器內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: