Spring02_基於XML的IOC

来源:https://www.cnblogs.com/codeaction/archive/2020/05/24/12953274.html
-Advertisement-
Play Games

本教程源碼請訪問: "tutorial_demo" 上篇教程我們學習瞭如何使用工廠模式解耦,把對象的創建由程式員交給自定義的工廠類,在這篇教程我們將學到如何使用Spring的IOC解決程式的耦合問題。 一、什麼是IOC IOC:Inversion of Control,控制反轉,將創建對象的權力交給 ...


本教程源碼請訪問:tutorial_demo

上篇教程我們學習瞭如何使用工廠模式解耦,把對象的創建由程式員交給自定義的工廠類,在這篇教程我們將學到如何使用Spring的IOC解決程式的耦合問題。

一、什麼是IOC

IOC:Inversion of Control,控制反轉,將創建對象的權力交給框架。過去創建對象由開發人員通過new的方式創建,有了IOC之後,開發人員不需要new了,只需要從Spring容器(我們可以認為是保存對象的容器)中獲取就可以了,創建對象的控制權發生了轉移,由開發人員轉移給了Spring容器或者說轉移給了Spring框架。這種控制權的轉移,我們稱之為控制反轉。

目的:減少電腦程式的耦合,解除代碼之間的依賴關係。

二、使用IOC(第一個Spring程式)

2.1、創建項目

  1. 在Idea中新建Maven工程;

  2. 工程創建完成後添加相應的坐標。

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>org.example</groupId>
        <artifactId>ioc</artifactId>
        <version>1.0-SNAPSHOT</version>
        <packaging>jar</packaging>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>5.2.6.RELEASE</version>
            </dependency>
        </dependencies>
    </project>
    

2.2、添加相關類

2.2.1、創建持久層介面

package org.codeaction.dao;

public interface IAccountDao {
    void saveAccount();
}

2.2.2、創建持久層介面實現類

package org.codeaction.dao.impl;

import org.codeaction.dao.IAccountDao;

public class AccountDaoImpl implements IAccountDao {
    @Override
    public void saveAccount() {
        System.out.println("賬戶保存成功");
    }
}

2.2.3、創建業務層介面

package org.codeaction.service;

public interface IAccountService {
    void saveAccount();
}

2.2.4、創建業務層介面實現類

package org.codeaction.service.impl;

import org.codeaction.dao.IAccountDao;
import org.codeaction.dao.impl.AccountDaoImpl;
import org.codeaction.service.IAccountService;

public class AccountServiceImpl implements IAccountService {
    private IAccountDao accountDao = new AccountDaoImpl();
    @Override
    public void saveAccount() {
        accountDao.saveAccount();
    }
}

註意這個類的實現,本教程最後會說明。

2.3、添加XML配置文件

XML文件在resource目錄下。

<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--把對象的創建交給spring來管理-->
    <bean id="accountDao" class="org.codeaction.dao.impl.AccountDaoImpl"></bean>
    <bean id="accountService" class="org.codeaction.service.impl.AccountServiceImpl"></bean>
</beans>

bean標簽作用:配置讓Spring創建對象。預設情況下調用無參構造函數,如果沒有無參構造函數則不能創建成功。

bean標簽屬性

  • id:為對象在容器中提供一個唯一標識,用於獲取對象;
  • class:指定類的全限定類名,用於反射創建對象,預設情況下調用無參構造函數;
  • scope:指定對象的作用範圍,預設是singleton(單例),3.1中會講到;
  • init-method:指定類中的初始化方法名稱;
  • destroy-method:指定類中銷毀方法名稱。

2.4、添加測試類

創建帶有main方法的類,用來進行測試

package org.codeaction.ui;

import org.codeaction.dao.IAccountDao;
import org.codeaction.service.IAccountService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AccountUI {
    public static void main(String[] args) {
        //1.獲取核心容器對象
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        //2.根據id獲取Bean對象,這個id是在bean標簽中配置的id
        IAccountService accountService = (IAccountService) context.getBean("accountService");
        IAccountDao accountDao = context.getBean("accountDao", IAccountDao.class);

        System.out.println(accountService);
        System.out.println(accountDao);
        accountService.saveAccount();
    }
}

運行main方法,控制台輸出如下:

org.codeaction.service.impl.AccountServiceImpl@754ba872
org.codeaction.dao.impl.AccountDaoImpl@146ba0ac
賬戶保存成功

2.4.1、BeanFactory和ApplicationContext的區別

  1. BeanFactory是Spring容器中的頂層介面,ApplicationContext是它的子介面;
  2. ApplicationContext只要一讀取配置文件,預設情況下就會創建對象;
  3. BeanFactory什麼時候使用時候創建對象;
  4. ApplicationContext用來創建單例對象,BeanFactory用來創建多例對象。

2.4.2、ApplicationContext介面的實現類

  1. ClassPathXmlApplicationContext可以載入類路徑下的配置文件,要求配置文件必須在類路徑下,不在的話,載入不了。
  2. FileSystemXmlApplicationContext可以載入磁碟任意路徑下的配置文件(必須有訪問許可權)
  3. AnnotationConfigApplicationContext用於讀取註解創建容器的,後面的文章會講到。

三、深入說明

3.1、bean的作用範圍和生命周期

bean的作用範圍由bean標簽中的scope屬性設置,scope屬性可以有如下值:

  • singleton:預設值,單例的;
  • prototype:多例的;
  • request:web項目中,Spring創建一個Bean的對象,將對象存入到request域中;
  • session:web項目中,Spring創建一個Bean的對象,將對象存入到session域中;
  • global session:web項目中,應用在Portlet環境。如果沒有Portlet環境那麼globalSession相當於session。

下麵我們重點說一下singleton和prototype。

3.1.1、singleton

一個應用只有一個對象的實例,它的作用範圍就是整個應用。
生命周期:

  • 對象出生:當應用載入,創建容器時,對象就被創建了;
  • 對象活著:只要容器在,對象一直活著;
  • 對象死亡:當應用卸載,銷毀容器時,對象就被銷毀了。

3.1.2、prototype

每次訪問對象時,都會重新創建對象實例。
生命周期:

  • 對象出生:當使用對象時,創建新的對象實例;
  • 對象活著:只要對象在使用中,就一直活著;
  • 對象死亡:當對象長時間不用時,被Java的垃圾回收器回收了。

3.1.3、案例(代碼基於第一個Spring程式)

3.1.3.1、修改業務層實現類

添加init和destroy方法

package org.codeaction.dao.impl;

import org.codeaction.dao.IAccountDao;

public class AccountDaoImpl implements IAccountDao {
    @Override
    public void saveAccount() {
        System.out.println("賬戶保存成功");
    }

    public void init() {
        System.out.println("dao init");
    }

    public void destroy() {
        System.out.println("dao destroy");
    }
}
3.1.3.2、修改持久層實現類

添加init和destroy方法

package org.codeaction.dao.impl;

import org.codeaction.dao.IAccountDao;

public class AccountDaoImpl implements IAccountDao {
    @Override
    public void saveAccount() {
        System.out.println("賬戶保存成功");
    }

    public void init() {
        System.out.println("dao init");
    }

    public void destroy() {
        System.out.println("dao destroy");
    }
}
3.1.3.3、修改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
        https://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--把對象的創建交給spring來管理-->
    <!--配置bean時指定初始化和銷毀方法及作用範圍-->
    <bean
            id="accountDao"
            class="org.codeaction.dao.impl.AccountDaoImpl"
            scope="singleton"
            init-method="init"
            destroy-method="destroy"></bean>
    <bean
            id="accountService"
            class="org.codeaction.service.impl.AccountServiceImpl"
            scope="prototype"
            init-method="init"
            destroy-method="destroy"></bean>
</beans>
3.1.3.4、修改測試類
package org.codeaction.ui;

import org.codeaction.dao.IAccountDao;
import org.codeaction.service.IAccountService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AccountUI {
    public static void main(String[] args) {
        //1.獲取核心容器對象
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        //2.根據id獲取Bean對象,這個id是在bean標簽中配置的id
        IAccountService accountService1 = (IAccountService) context.getBean("accountService");
        IAccountDao accountDao1 = (IAccountDao) context.getBean("accountDao");
        IAccountService accountService2 = (IAccountService) context.getBean("accountService");
        IAccountDao accountDao2 = (IAccountDao) context.getBean("accountDao");

        System.out.println("accountDao1 == accountDao2 ? " + (accountDao1 == accountDao2));
        System.out.println("accountService1 == accountService2 ? " + (accountService1 == accountService2));
        //容器銷毀
        context.close();
    }
}

單步調試該程式,輸出如下:

dao init
service init
service init
accountDao1 == accountDao2 ? true
accountService1 == accountService2 ? false
dao destroy

通過輸出我們驗證了:

  • singleton的bean對象在容器中只會創建一次,並且創建容器時,就被創建了;
  • prototype的bean對象在容器中能夠創建多次,當使用時,就創建新的對象;
  • singleton的bean對象在容器銷毀時,也被銷毀;
  • prototype的bean對象會被垃圾回收(通過控制台觀察不到)。

3.2、實例化bean的三種方式

3.2.1、三種方式說明

  1. 使用預設無參構造函數;
  2. 使用靜態工廠的方法創建對象;
  3. 使用實例工廠的方法創建對象。

3.2.2、案例(代碼基於第一個Spring程式)

3.2.2.1、創建靜態工廠類
package org.codeaction.factory;

import org.codeaction.service.IAccountService;
import org.codeaction.service.impl.AccountServiceImpl;

/**
 * 模擬一個靜態工廠,創建業務層實現類
 */
public class StaticFactory {
    public static IAccountService  createAccountService() {
        return new AccountServiceImpl();
    }
}
3.2.2.2、創建實例工廠類
package org.codeaction.factory;

import org.codeaction.service.IAccountService;
import org.codeaction.service.impl.AccountServiceImpl;

/**
 * 模擬一個實例工廠,創建業務層實現類
 * 此工廠創建對象,必須現有工廠實例對象,再調用方法
 */
public class InstanceFactory {
    public IAccountService createAccountService(){
        return new AccountServiceImpl();
    }
}
3.2.2.3、修改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
        https://www.springframework.org/schema/beans/spring-beans.xsd">
    <!-- 方式1:使用預設的無參構造方式 -->
    <bean id="accountService1" class="org.codeaction.service.impl.AccountServiceImpl"></bean>

    <!--
        方式2:使用靜態工廠的方法創建對象
        id屬性:指定bean的id,用於從容器中獲取
        class屬性:指定靜態工廠的全限定類名
        factory-method屬性:指定生產對象的靜態方法
    -->
    <bean
	id="accountService2"
        class="org.codeaction.factory.StaticFactory"
        factory-method="createAccountService"></bean>
    <!--
        方式3:使用實例工廠的方法創建對象
        先把工廠的創建交給spring來管理。
        然後在使用工廠的bean來調用裡面的方法。
        factory-bean屬性:用於指定實例工廠bean的id。
        factory-method屬性:用於指定實例工廠中創建對象的方法。
    -->
    <bean id="factory" class="org.codeaction.factory.InstanceFactory"></bean>
    <bean id="accountService3" factory-bean="factory" factory-method="createAccountService"></bean>
</beans>
3.2.2.4、修改測試類
package org.codeaction.ui;

import org.codeaction.dao.IAccountDao;
import org.codeaction.service.IAccountService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AccountUI {
    public static void main(String[] args) {
        //1.獲取核心容器對象
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        //2.根據id獲取Bean對象,這個id是在bean標簽中配置的id
        IAccountService accountService1 = (IAccountService) context.getBean("accountService1");
        IAccountService accountService2 = (IAccountService) context.getBean("accountService2");
        IAccountService accountService3 = (IAccountService) context.getBean("accountService3");

        System.out.println(accountService1);
        System.out.println(accountService2);
        System.out.println(accountService3);
    }
}

運行測試類,控制台輸出如下:

org.codeaction.service.impl.AccountServiceImpl@42dafa95
org.codeaction.service.impl.AccountServiceImpl@6500df86
org.codeaction.service.impl.AccountServiceImpl@402a079c

四、目前存在的問題

通過這篇教程的學習,我們對IOC有了一個初步的認識。通過IOC將對象創建的權力交給Spring容器,實現控制權的轉移。本篇教程2.2.4中,創建業務層介面的實現類,裡面的屬性依然使用new對象的方式賦值,在這裡依然沒有解耦,service和dao的對象依然攪在一起,那麼怎麼解決這個問題呢?下一篇我們將學習DI(依賴註入),DI就可以解決目前存在的問題。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 老實說,在實際編程中,訪問者設計模式應用的並不多,至少我是這樣認為的,因為它的主要使用場景並不多。那麼肯定會有人問,訪問者模式的主要使用場景是什麼呢?繼續往下看便知。新聞聯播看多了之後首先要說的是,設計模式中的“訪問者”和現實生活中的“訪問者”其本質是一回事。雖然設計模式中的不太熟悉,但現實生活中的 ...
  • MMU存在的意義 [導讀] 本文從記憶體管理的發展歷程角度層層遞進,介紹MMU的誕生背景,工作機制。而忽略了具體處理器的具體實現細節,將MMU的工作原理從概念上比較清晰的梳理了一遍。 MMU誕生之前: 在傳統的批處理系統如DOS系統,應用程式與操作系統在記憶體中的佈局大致如下圖: 應用程式直接訪問物理內 ...
  • [導讀] 前文描述了棧的基本概念,本文來聊聊堆是怎麼會事兒。RT Thread 在社區廣受歡迎,閱讀了其內核代碼,實現了堆的管理,代碼設計很清晰,可讀性很好。故一方面瞭解RT Thread內核實現,一方面可以弄清楚其堆的內部實現。將學習體會記錄分享,希望對於堆的理解及實現有一個更深入的認知。 註,文 ...
  • 通常大家開發大部分是本地git push 提交,伺服器上git pull 手動更新。git 可以使用webhook實現自動部署。webhook是倉庫平臺的一個鉤子事件,通過hook 鉤子監聽代碼,回調通知(通知地址就是你在各個git倉庫平臺中填寫的webhook地址,一般在你的某個項目倉庫如mypr ...
  • 根據碎片的生命周期,我們知道onAttach()方法首先會被執行,因此在這裡做一些數據初始化的操作,比如調用getNews()方法獲取幾條模擬的新聞數據,以及完成NewsAdapter的創建,然後在onCreateView()方法中載入了news_title_frag佈局,並給新聞列表的ListVi ...
  • 1/ 概述 利用Spring Boot作為基礎框架,Spring Security作為安全框架,WebSocket作為通信框架,實現點對點聊天和群聊天。 2/ 所需依賴 Spring Boot 版本 1.5.3,使用MongoDB存儲數據(非必須),Maven依賴如下: 配置文件內容: 大致程式結構 ...
  • 新開一坑——Elements of Financial Risk Management in Python 用 python 完成 " Elements of Financial Risk Management (Second Edition)" 一書的課後實踐練習,希望年底之前能完成吧。 項目地址 ...
  • 最近開始學Go語言,但是在使用VS Code 編寫Go的時候出現了插件無法下載的問題。最初我的解決辦法也是從github下載再安裝,但是我並不喜歡這種做法,因為我要在多台pc上使用VS Code編寫Go,所以我覺要重覆多次很麻煩,而且插件的安裝也非常麻煩,我曾經一度想放棄學習Go語言,沒錯因為安裝插 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...