Spring之 IOC&依賴注入

0x01、Spring

1什麼是Spring

​ Spring 是一個開源框架,是為了解決企業應用程式開發複雜性而創建的(解耦)。

​ 框架的主要優勢之一就是其分層架構,分層架構允許您選擇使用哪一個組件,同時為 J2EE 應用程式開發提供集成的框架。

​ 簡單來說,Spring是一個分層的JavaSE/EE full-stack(一站式) 輕量級開源框架。

​ 一站式:Spring提供了三層解決方案.

0x02、IOC

它的核心思想就是:
1、通過Bean工廠讀取配置文件使用反射創建對象。
2、把創建出來的對象都存起來,當使用者需要對象的時候,不再自己創建對象,而是調用Bean工廠的方法從容器中獲取對象

這裡面要解釋兩個問題:
第一個:存哪去?
分析:由於我們是很多對象,肯定要找個集合來存。這時候有 Map 和 List 供選擇。
到底選 Map 還是 List 就看我們有沒有查找需求。有查找需求,選 Map。
所以我們的答案就是:在應用載入時,創建一個 Map,用於存放三層對象。我們把這個 map 稱之為容器。
第二個: 什麼是工廠?
工廠就是負責給我們從容器中獲取指定對象的類。這時候我們獲取對象的方式發生了改變。
原來:我們在獲取對象時,都是採用 new 的方式。 是主動的。

1、Spring的IOC入門案例

步驟:

1. 創建Maven工程, 添加坐標

2. 準備好介面和實現類

3. 創建spring的配置文件 (applicationContext.xml), 配置bean標籤

4. 創建工廠對象 獲得bean 調用

  • 1.1、引入spring的依賴(使用5.0.2版本)
<dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.0.2.RELEASE</version>
    </dependency>
  • 1.2、介面和實現類

    • UserServiceImpl.java
    public interface UserService {
        String getName();
    }
    
    • UserServiceImpl.java

      ```java
      

import com.itheima.service.UserService;

public class UserServiceImpl implements UserService{
public void init(){
System.out.println(“UserServiceImpl對象創建了…”);
}

public void destroy(){
    System.out.println("UserServiceImpl對象銷毀了...");
}

@Override
public String getName() {
    return "周傑棍";
}

}
“`

  • 1.3、在類的根路徑下創建spring的配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="//www.springframework.org/schema/beans"
       xmlns:xsi="//www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="//www.springframework.org/schema/beans
       //www.springframework.org/schema/beans/spring-beans.xsd">
    <!--
        每一個實現類就對應一個bean標籤
            id屬性: 對象的唯一標識,根據這個唯一標識,就可以從核心容器中獲取對象
            class屬性: 對象所屬的實現類的全限定名
    -->
    <bean class="org.example.Impl.UserServiceImpl" id="userService"></bean>
</beans>
  • 1.4、測試程式碼
public class AppTest 
{
    @Test
    public void test01(){
        //調用UserServiceImpl類的方法
        //1. 創建spring的核心容器(載入類路徑下的xml配置文件的核心容器)
        //在創建核心容器的時候,就已經讀取了整個spring.xml配置文件,就已經創建好了它裡面的bean標籤對應的對象
        //並且對象都放到核心容器中了
        ApplicationContext act = new ClassPathXmlApplicationContext("classpath:springConfig.xml");
        //ApplicationContext act = new FileSystemXmlApplicationContext("d:/a/spring.xml");

        //2. 調用核心容器的方法,根據id獲取對象
        UserService userService = (UserService) act.getBean("userService");

        System.out.println(userService.getName());
    }
}

2、Spring配置文件下的Bean標籤 配置

  1. 配置文件詳解(Bean標籤)
  2. bean的作用範圍和生命周期

(一)、配置文件詳解(Bean標籤)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="//www.springframework.org/schema/beans"
       xmlns:xsi="//www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="//www.springframework.org/schema/beans
       //www.springframework.org/schema/beans/spring-beans.xsd">
    <!--
        每一個實現類就對應一個bean標籤
            id屬性: 對象的唯一標識,根據這個唯一標識,就可以從核心容器中獲取對象
            class屬性: 對象所屬的實現類的全限定名
            scope屬性: 對象的範圍
                 1. singleton 單例(默認)
                 2. prototype 多例
            lazy-init: 配置懶載入,核心容器創建的時候是否創建出該類對象
            init-method: 配置類的對象初始化的時候,要調用哪個方法
            destroy-method: 配置這個類的對象銷毀的時候,要調用哪個方法
            單例模式下(默認沒有開啟懶載入),由核心容器進行管理的對象什麼時候創建什麼時候銷毀?
            1. 核心容器創建的時候,會創建出它所配置的所有類的對象
            2. 核心容器銷毀的時候,它裡面的對象才會被銷毀

            多例模式下,由spring管理的對象什麼時候創建什麼時候銷毀
            1. 當核心容器調用getBean(id)的時候,創建對象
            2. 垃圾回收機制才能銷毀這個對象
    -->
    <bean class="com.itheima.service.impl.UserServiceImpl"
          id="userService"
          scope="prototype" lazy-init="false"
          init-method="init"
          destroy-method="destroy"></bean>
</beans>
  • id或者name屬性

    ​ 用於標識bean , 其實id 和 name都必須具備唯一標識 ,兩種用哪一種都可以。但是一定要唯一、 一般開發中使用id來聲明.

  • class屬性: 用來配置要實現化類的全限定名

  • scope屬性: 用來描述bean的作用範圍

    ​ singleton: 默認值,單例模式。spring創建bean對象時會以單例方式創建。(默認)

    ​ prototype: 多例模式。spring創建bean對象時會以多例模式創建。

    ​ request: 針對Web應用。spring創建對象時,會將此對象存儲到request作用域。(不用管)

    ​ session: 針對Web應用。spring創建對象時,會將此對象存儲到session作用域。(不用管)

  • init-method屬性:spring為bean初始化提供的回調方法

  • destroy-method屬性:spring為bean銷毀時提供的回調方法. 銷毀方法針對的都是單例bean , 如果想銷毀bean , 可以關閉工廠

(二)、bean的作用範圍和生命周期

  • 單例對象: scope=”singleton”,一個應用只有一個對象的實例。它的作用範圍就是整個引用。
    1. 核心容器創建的時候,會創建出它所配置的所有類的對象
    2. 核心容器銷毀的時候,它裡面的對象才會被銷毀
  • 多例對象: scope=”prototype”,每次訪問對象時,都會重新創建對象實例。
    1. 當核心容器調用getBean(id)的時候,創建對象
    2. 垃圾回收機制才能銷毀這個對象

3、Spring的工廠模式

ApplicationContext介面的三種實現類

1、ClassPathXmlApplicationContext:它是從類的根路徑下載入配置文件 

2、FileSystemXmlApplicationContext:它是從磁碟路徑上載入配置文件,配置文件可以在磁碟的任意位置。

3、AnnotationConfigApplicationContext:當我們使用註解配置容器對象時,需要使用此類來創建 spring 容器。它用來讀取註解。
    
    繼承自 ApplicationContext

ApplicationContext載入方式是框架啟動時開始創建所有單例的bean,存到了容器裡面

  • 非懶載入: 在核心容器創建的時候,創建出所有的bean對象,存到核心容器中
  • 懶載入: 第一次調用getBean()的時候,創建出bean對象,存到核心容器中

4、實例化Bean

​ 需要實例化的類,提供無參構造方法

​ 配置程式碼

<bean class="com.itheima.service.impl.UserServiceImpl" id="userService"></bean>

0x03、依賴注入

依賴注入全稱是 dependency Injection 翻譯過來是依賴注入.其實就是如果我們託管的某一個類中存在屬性,需要spring在創建該類實例的時候,順便給這個對象裡面的屬性進行賦值。 這就是依賴注入。

​ 現在, Bean的創建交給Spring了, 需要在xml裡面進行註冊

​ 我們交給Spring創建的Bean裡面可能有一些屬性(欄位), Spring幫我創建的同時也把Bean的一些屬性(欄位)給賦值, 這個賦值就是注入.

簡單的來說如下:

  1. 註冊: 把bean的創建交給Spring
  2. 依賴注入: bean創建的同時, bean裡面可能有一些欄位需要賦值, 這個賦值交給Spring, 這個過程就是依賴注入

測試程式碼(環境):

  • Controller.UserController
import com.idea.service.UserService;

public class UserController{
    private UserService userService;
    
    public String getName(){
        return userService.getName();
    }
}
  • service.UserService
public interface UserService {
    String getName();
}
  • service.Impl.UserServiceImpl
import com.idea.dao.UserDao;
import com.idea.service.UserService;

public class UserServiceImpl implements UserService {
    private UserDao userDao;


    @Override
    public String getName() {
        return userDao.getName();
    }
}

  • dao.UserDao:
public interface UserDao {
    String getName();
}
  • dao.Impl.UserDaoImpl
import com.idea.dao.UserDao;

public class UserDaoImpl implements UserDao {
	//模擬資料庫執行語句,並得到名字為王五
    @Override
    public String getName(){
        return "王五";
    }
}

然後我們創建springConfig.xml的配置文件,我們需要把實現類放進來

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="//www.springframework.org/schema/beans"
       xmlns:xsi="//www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="//www.springframework.org/schema/beans
       //www.springframework.org/schema/beans/spring-beans.xsd">
  
    <bean id="UserController" class="com.idea.controller.UserController"></bean>
    <bean id="UserService" class="com.idea.service.Impl.UserServiceImpl"></bean>
    <bean id="UserDao" class="com.idea.dao.Impl.UserDaoImpl"></bean>
</beans>
  • 測試程式碼:(最外層的Controller不用解耦)
import com.idea.controller.UserController;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class demo {
    @Test
    public void test01(){
        //創建容器
        ApplicationContext act = new ClassPathXmlApplicationContext("classpath:springConfig.xml");
        
        //1. 創建UserController對象
        UserController userController = (UserController) act.getBean("UserController");
        
        //2. 調用UserController對象的getName()方法
        String name = userController.getName();
        System.out.println("獲取到的name為:" + name);
    }
}

1、構造方法方式注入

通過上面的環境搭建,我們執行一下

發現調用失敗了;UserServiceUserDao還沒有賦值。但是我們不能直接在裡面賦值,這樣就耦合了,這時候就用到了依賴注入

1、構造方法賦值

有參構造方法賦值的時候,就必須要有無參構造方法,不然會報錯

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="//www.springframework.org/schema/beans"
       xmlns:xsi="//www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="//www.springframework.org/schema/beans
       //www.springframework.org/schema/beans/spring-beans.xsd">
    <!--
        它裡面的UserService屬性要進行賦值
        使用有參構造進行屬性的注入,使用<constructor-arg>標籤
    -->
    <bean id="UserController" class="com.idea.controller.UserController">
        <constructor-arg name="userService" ref="UserService"></constructor-arg>
    </bean>
    <!--
        它裡面的UserDao屬性需要賦值
    -->
    <bean id="UserService" class="com.idea.service.Impl.UserServiceImpl"></bean>
    <bean id="UserDao" class="com.idea.dao.Impl.UserDaoImpl"></bean>
</beans>
  • 1、bean是實例化出對象,id就是對象名,ref="UserService"就是獲取UserService對象,要傳進去的內容

  • 2、name="userService"是屬性名,也就是要傳的參數

完整程式碼

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="//www.springframework.org/schema/beans"
       xmlns:xsi="//www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="//www.springframework.org/schema/beans
       //www.springframework.org/schema/beans/spring-beans.xsd">
    <!--
        它裡面的UserService屬性要進行賦值
        使用有參構造進行屬性的注入,使用<constructor-arg>標籤
    -->
    <bean id="UserController" class="com.idea.controller.UserController">
        <constructor-arg name="userService" ref="UserService"></constructor-arg>
    </bean>
    <!--
        它裡面的UserDao屬性需要賦值
    -->
    <bean id="UserService" class="com.idea.service.Impl.UserServiceImpl">
        <constructor-arg name="userDao" ref="UserDao"></constructor-arg>
    </bean>
    <bean id="UserDao" class="com.idea.dao.Impl.UserDaoImpl"></bean>
</beans>

2、set方法方式的注入

要通過set方法注入,那就得有setter

  • springConfig.xml

name:就是setter設置的參數名
ref: 對象UserService賦值給name標籤的userService

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="//www.springframework.org/schema/beans"
       xmlns:xsi="//www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="//www.springframework.org/schema/beans
       //www.springframework.org/schema/beans/spring-beans.xsd">
    <!--
        它裡面的UserService屬性要進行賦值
        使用set方法進行屬性的注入
    -->
    <bean id="UserController" class="com.idea.controller.UserController">
        <property name="userService" ref="UserService"></property>
    </bean>
    <!--
        它裡面的UserDao屬性需要賦值
    -->
    <bean id="UserService" class="com.idea.service.Impl.UserServiceImpl">
        <property name="userDao" ref="UserDao"></property>
    </bean>
    <bean id="UserDao" class="com.idea.dao.Impl.UserDaoImpl"></bean>
</beans>
  • UserController
import com.idea.service.UserService;

public class UserController{
    private UserService userService;

    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    public String getName(){
        return userService.getName();
    }
}

  • UserServiceImpl
import com.idea.dao.UserDao;
import com.idea.service.UserService;

public class UserServiceImpl implements UserService {
    private UserDao userDao;

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public String getName() {
        return userDao.getName();
    }
}

0x04、番外&總結

今天內容有IOC,和依賴注入

1、IOC是什麼?

IOC就是spring.xml中bean標籤:讓我們不用去new對象,讓spring實例化對象,進行解耦

Tags: