IOC -> XML

概念和作用

程序耦合

在軟件工程中,耦合指的就是就是對象之間的依賴性。對象之間的耦合越高,維護成本越高。因此對象的設計應使類和構件之間的耦合最小。軟件設計中通常用耦合度和內聚度作爲衡量模塊獨立程度的標準。劃分模塊的一個準則就是高內聚低耦合

解決耦合思路:反射 + 配置文件,減少 new 關鍵字的使用

工廠模式解耦

實際開發中我們可以把三層的對象都使用配置文件配置起來,當啓動服務器應用加載的時候,讓一個類中的方法通過讀取配置文件,把這些對象創建出來並存起來。在接下來的使用的時候,直接拿過來用。這個讀取配置文件,創建和獲取三層對象的類就是工廠。

控制反轉

問題一:對象存哪 ?

由於我們是很多對象,肯定要找個集合來存,這時候有 MapList 供選擇。由於我們有查找需求,所以選擇 Map,把這個 Map稱爲容器

問題二:什麼是工廠 ?

工廠就是負責給我們從容器中獲取指定對象的類

問題三:怎麼獲取對象 ?

主動方式:採用 new 的方式

在這裏插入圖片描述
被動方式:向工廠要,工廠爲我們查找或者創建對象(這就是控制反轉思想

在這裏插入圖片描述

IOC 入門案例

引入依賴

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.0.2.RELEASE</version>
    </dependency>
</dependencies>

創建接口及其配置類

public interface AccountService {
    void saveAccount();
}

import com.service.AccountService;
public class AccountServiceImpl implements AccountService {
    @Override
    public void saveAccount() {
        System.out.println("調用了 AccountServiceImpl 的 save 方法");
    }
}

創建配置文件

<?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">
    <bean id="accountService" class="com.service.impl.AccountServiceImpl">			</bean>
</beans>

主方法進行測試

import com.service.AccountService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class ApplicationTest {
    public static void main(String[] args) {
        // 使用 ApplicationContext
        ApplicationContext application = new ClassPathXmlApplicationContext("application.xml");
        AccountService accountService = application.getBean("accountService", AccountService.class);
        accountService.saveAccount();
        
        //使用 BeanFactory
        Resource resoure = new ClassPathResource("application.xml");
        BeanFactory factory = new XmlBeanFactory(resoure);
        AccountService as = (AccountService) factory.getBean("accountService");
        as.saveAccount();
    }
}

執行結果

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-5lRreWG3-1587955611402)(F:\筆記\Spring\image\IOC 入門案例執行結果.png)]

瞭解常用類

ApplicationCOntext的三個常用類

ClassPathXmlApplicationContext:它可以加載類路徑下的配置文件,要求配置文件必須在類路徑下

FileSystemXmlApplicationContext:它可以加載磁盤任意路徑下的配置文件(必須要有訪問權限)

AnnotationConfigApplicationContext基於註解使用的,它不需要配置文件,採用配置類和各種註解來配置

核心容器的兩個接口

ApplicationContext:單例適用,它在構建核心容器時,創建對象採取的策略是採用立即加載,也就是說,只要已讀取完配置文件馬上就創建配置文件中配置的對象

BeanFactory:多例適用,它在構建核心容器時,創建對象採取的策略是採用延遲加載的方式,也就是說,什麼時候根據id獲取對象了,什麼時候才真正的創建對象

IOC 中的 Bean 標籤

bean 標籤的常用屬性

屬性 作用
id 給對象在容器中提供一個唯一標識,用於獲取對象
class 指定類的全限定類名。用於反射創建對象(默認調用無參構造函數)
scope 指定對象的作用範圍(具體見下面的scope表)
name 可指定 id、name(用逗號、分號、空格隔開)
autowire 自動裝配(具體見下面的autowire表)
init-method bean 屬性設置完成後,會調用這個方法
destroy-method bean 銷燬後的回調方法
lazy-init 是否懶加載(如果被非懶加載的bean依賴了,那麼也就不能懶加載了)
factory-bean 用於指定實例工廠 bean 的 id
factory-method 用於指定實例工廠中創建對象的方法
property 設置屬性的值
constructor-arg 指定構造參數

scope屬性詳解

屬性值 作用
singleton 默認值,表示單例的
prototype 表示多例的
request 在 web 項目中,Spring 創建一個 Bean 對象,將對象存在 request 域中
session 在 web 項目中,Spring 創建一個 Bean 對象,將對象存在 session 域中
global session 在 web 項目中,應用在 Portlet 環境,如果沒有 Portlet 環境那麼就相當於session

證明scope(使用上面定義的 accountService):

<!--測試單例多例-->
<bean id="accountService" class="com.service.impl.AccountServiceImpl" scope="`singleton`"></bean>
//測試 scope:singleton
AccountService accountService1 = application.getBean("accountService", AccountService.class);
AccountService accountService2 = application.getBean("accountService", AccountService.class);
System.out.println(accountService1 == accountService2);*///true
//測試 scope : prototype
AccountService accountService3 = application.getBean("accountService", AccountService.class);
AccountService accountService4 = application.getBean("accountService", AccountService.class);
System.out.println(accountService3 == accountService4);//false

實例化bean的方式

使用默認無參構造函數:在 spring 的配置文件中使用 bean標籤,配以 idclass 屬性之後,且沒有其他屬性和標籤時採用的是默認構造函數創建 bean對象,此時如果類中沒有默認構造函數,則對象無法創建

<bean id="accountService" class="com.service.impl.AccountServiceImpl" scope="singleton"></bean>

使用普通工廠中的方法創建對象:使用某個類中的方法創建對象,並存入 spring 容器中

<!-- factory-bean屬性:用於指定實例工廠bean的id -->
<!-- factory-method屬性:用於指定實例工廠中創建對象的方法 -->

<!--創建工廠 Bean-->
<bean id="instanceFactory" class="com.factory.InstanceFactory"></bean>
<!--使用工廠中的普通方法創建對象-->
<bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService"></bean>
// java 工廠
public class InstanceFactory {
    private AccountService accountService = new AccountServiceImpl();
    public AccountService getAccountService(){
        return accountService;
    }
}

使用工廠中的靜態方法創建對象: 使某個類中的靜態方法創建對象,並存入spring容器中

<!-- id屬性:指定bean的id,用於從容器中獲取 -->
<!-- class屬性:指定靜態工廠的全限定類名 -->
<!-- factory-method屬性:指定生產對象的靜態方法 -->

<!--使用靜態方法創建對象-->
<bean id="accountService" class="com.factory.StaticFactory" factory-method="getAccountService"></bean>
// java 工廠
public class StaticFactory {
    private static AccountService accountService = new AccountServiceImpl();
    public static AccountService getAccountService(){
        return accountService;
    }
}

Spring 依賴注入

構造函數注入

屬性值 作用
type 用於指定要注入數據的數據類型,該數據類型也是構造函數中某個或某些參數的類型
index 用於指定要注入的數據給構造函數中指定索引位置的參數賦值,索引的位置是從0開始
name 用於指定給構造函數中指定名稱的參數賦值(常用名稱)
value 用於提供基本類型和 String 類型的數據
ref 用於指定其他的 bean 類型數據,它指的就是在 SpringIOC 核心容器中出現過的bean對象

測試下列情況所使用的 Person

import java.util.Date;
public class Person {
    private String name;
    private int age;
    private Date date;
    
    // 下列情況演示中使用的構造方法
    public Person(String name,int age,Date date){
        this.name = name;
        this.age = age;
        this.date = date;
    } 
}

情況一:index + value / index + ref

<bean id="date" class="java.util.Date"></bean>
<bean id="p" class="com.domain.Person">
    <constructor-arg index="0" value="張三"></constructor-arg>
    <constructor-arg index="1" value="15"></constructor-arg>
    <constructor-arg index="2" ref="date"></constructor-arg>
</bean>

情況二:type + value / type + ref

<bean id="date" class="java.util.Date"></bean>
<bean id="p1" class="com.domain.Person">
    <constructor-arg type="java.lang.String" value="張三"></constructor-arg>
    <constructor-arg type="int" value="15"></constructor-arg>
    <constructor-arg type="java.util.Date" ref="date"></constructor-arg>
</bean>

情況三:name + value / name + ref

<bean id="date" class="java.util.Date"></bean>
<bean id="p2" class="com.domain.Person">
    <constructor-arg name="name" value="張三"></constructor-arg>
    <constructor-arg name="age" value="15"></constructor-arg>
    <constructor-arg name="date" ref="date"></constructor-arg>
</bean>

測試驗證

// 測試的代碼
ApplicationContext application = new ClassPathXmlApplicationContext("application.xml");
Person p = application.getBean("p", Person.class);
Person p1 = application.getBean("p1", Person.class);
Person p2 = application.getBean("p2", Person.class);
System.out.println(p);
System.out.println(p1);
System.out.println(p2);

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-XkNeeO5p-1587955611405)(F:\筆記\Spring\image\構造方法注入.png)]

Set 方法注入

屬性值 作用
name 用於指定注入時調用的 set 方法的名稱
value 用於提供基本類型和 String 類型的數據
ref 用於指定其他的 bean 類型數據,指的是在 SpringIOC 核心容器中出現過的bean 對象

說明:使用的是上面的 Person 類,但是需要添加 Getter / Setter方法,同時跟上面的驗證使用的是同樣的方法。

<bean id="p3" class="com.domain.Person">
    <property name="name" value="張三"></property>
    <property name="age" value="15"></property>
    <property name="date" ref="date"></property>
</bean>

複雜類型的注入

給數組類型注入

測試下列情況所使用的的 School

import java.util.Arrays;
public class School {
    private String[] schoolNames = new String[3];
    public School() {
    }
    public School(String[] schoolNames) {
        this.schoolNames = schoolNames;
    }
    public String[] getSchoolNames() {
        return schoolNames;
    }
    public void setSchoolNames(String[] schoolNames) {
        this.schoolNames = schoolNames;
    }
    toString();  // 自行補充
}

情況一 :使用構造方法

<bean id="s2" class="com.domain.School">
    <constructor-arg name="schoolNames">
        <array>
            <value>重慶大學</value>
            <value>重慶文理學院</value>
            <value>重慶師範大學</value>
        </array>
    </constructor-arg>
</bean>

情況二:使用 set 方法

<bean id="s1" class="com.domain.School">
    <property name="schoolNames">
        <array>
            <value>重慶大學</value>
            <value>重慶師範大學</value>
            <value>重慶文理學院</value>
        </array>
    </property>
</bean>

List 類型進行注入

測試下列情況所使用的的 Dates

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class Dates {
    private List<Date> dates = new ArrayList<>();
    public Dates() {
    }
    public Dates(List<Date> dates) {
        this.dates = dates;
    }
    public List<Date> getDates() {
        return dates;
    }
    public void setDates(List<Date> dates) {
        this.dates = dates;
    }
    toString();  // 自行補充
}

情況一 :使用構造方法

<bean id="dates1" class="com.domain.Dates">
    <constructor-arg name="dates">
        <list>
            <ref bean="date"></ref>
            <ref bean="date"></ref>
            <ref bean="date"></ref>
        </list>
    </constructor-arg>
</bean>

情況二:使用 set 方法

<bean id="dates2" class="com.domain.Dates">
    <property name="dates">
        <list>
            <ref bean="date"></ref>
            <ref bean="date"></ref>
        </list>
    </property>
</bean>

Set 類型進行注入

測試下列情況所使用的的 Maps

import java.util.*;
public class Dates {
    private Set<Date> dates = new HashSet<>();
    public Dates() {
    }
    public Dates(Set<Date> dates) {
        this.dates = dates;
    }
    public Set<Date> getDates() {
        return dates;
    }
    public void setDates(Set<Date> dates) {
        this.dates = dates;
    }
    toString(); // 自行添加
}

情況一 :使用構造方法

<bean id="set2" class="com.domain.Dates">
    <constructor-arg name="dates">
        <set>
            <ref bean="date"></ref>
            <ref bean="date"></ref>
            <ref bean="date"></ref>
        </set>
    </constructor-arg>
</bean>

情況二:使用 set 方法

<bean id="set1" class="com.domain.Dates">
    <property name="dates">
        <set>
            <ref bean="date"></ref>
            <ref bean="date"></ref>
            <ref bean="date"></ref>
        </set>
    </property>
</bean>

Map 類型進行注入

測試下列情況所使用的的 Maps

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class Maps {
    private Map<String, Date> map = new HashMap<>();
    public Maps() {
    }
    public Maps(Map<String, Date> map) {
        this.map = map;
    }
    public Map<String, Date> getMap() {
        return map;
    }
    public void setMap(Map<String, Date> map) {
        this.map = map;
    }
    toString();  // 自行添加
}

情況一 :使用構造方法

<bean id="m1" class="com.domain.Maps">
    <constructor-arg name="map">
        <map>
            <entry key="1" value-ref="date"></entry>
            <entry key="2">
                <ref bean="date"></ref>
            </entry>
        </map>
    </constructor-arg>
</bean>

情況二:使用 set 方法

<bean id="m2" class="com.domain.Maps">
    <property name="map">
        <map>
            <entry key="1" value-ref="date"></entry>
            <entry key="2">
                <ref bean="date"></ref>
            </entry>
        </map>
    </property>
</bean>

autowire 屬性詳解

屬性值 作用
no 默認值,不啓動自動裝配
byName 根據屬性名自動裝配。在容器中根據名字查找與屬性名完全一致的 bean,並將其與屬性自動裝配
byType 如果容器中存在一個與指定屬性類型相同的 bean ,那麼將與該屬性自動裝配;如果存在多個該類型 bean,那麼拋出異常,並指出不能使用 byType 方式進行自動裝配;如果沒有找到相匹配的bean,則什麼事都不發生,也可以通過設置 dependency-check=“objects” 讓Spring拋出異常
constructor 與 byType 方式類似,不同之處在於它應用於構造器參數。如果容器中沒有找到與構造器參數類型一致的 bean, 那麼拋出異常
default 由上級標籤的 default-autowire 屬性確定。

證明 autowire 屬性所使用的類

public class Man {
    private Person person;
    public Man() {
    }
    public Man(Person person) {
        this.person = person;
    }
    public Person getPerson() {
        return person;
    }
    public void setPerson(Person person) {
        this.person = person;
    }
   	toString(); // 自行添加
}
import java.util.Date;
public class Person {
    private String name;
    private int age;
    private Date date;
    public Person() {
    }
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public Person(String name,int age,Date date){
        this.name = name;
        this.age = age;
        this.date = date;
    }
    public Date getDate() {
        return date;
    }
    public void setDate(Date date) {
        this.date = date;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
	toString(); // 自行添加
}

測試代碼:

ApplicationContext application = new ClassPathXmlApplicationContext("application.xml");
//測試 autowire
Person person = application.getBean("person",Person.class);
Man man = application.getBean("man",Man.class);
System.out.println(person);
System.out.println(man);

證明 byName 屬性

<bean id="date" class="java.util.Date"></bean>
<bean id="person" class="com.domain.Person">
    <property name="name" value="LEE"/>
    <property name="age" value="20"/>
    <property name="date" ref="date"/>
 </bean>
<bean id="man" class="com.domain.Man" autowire="byName"></bean>

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-NNeZj8oL-1587955611408)(F:\筆記\Spring\image\byName自動注入成功.png)]


證明 byType 屬性

情況一 :IOC 容器中只有一個該類型的 bean

<bean id="date" class="java.util.Date"></bean>
<bean id="person" class="com.domain.Person">
    <property name="name" value="LEE"/>
    <property name="age" value="20"/>
    <property name="date" ref="date"/>
 </bean>
<bean id="man" class="com.domain.Man" autowire="byType"></bean>

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-jdZu6Mxs-1587955611410)(F:\筆記\Spring\image\byName自動注入成功.png)]

情況二:IOC 容器中有多個該類型的 bean

<bean id="date" class="java.util.Date"></bean>
<bean id="p3" class="com.domain.Person">
    <property name="name" value="張三"></property>
    <property name="age" value="15"></property>
    <property name="date" ref="date"></property>
</bean>
<bean id="person" class="com.domain.Person">
    <property name="name" value="LEE"/>
    <property name="age" value="20"/>
    <property name="date" ref="date"/>
 </bean>
<bean id="man" class="com.domain.Man" autowire="byType"></bean>

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-lQdYKyBY-1587955611412)(F:\筆記\Spring\image\byType錯誤情況.png)]


測試 constructor

情況一:有相對應的構造器

<bean id="date" class="java.util.Date"></bean>
<bean id="person" class="com.domain.Person">
    <property name="name" value="LEE"/>
    <property name="age" value="20"/>
    <property name="date" ref="date"/>
 </bean>
<bean id="man" class="com.domain.Man" autowire="constructor"></bean>

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-jIwJrgyx-1587955611413)(F:\筆記\Spring\image\byName自動注入成功.png)]

情況二:沒有相對應的構造器(還是使用情況一的配置,只是修改 Man 類)

public class Man {
    private Person person;
    public Man() {
    }
    public Person getPerson() {
        return person;
    }
    public void setPerson(Person person) {
        this.person = person;
    }
    toString(); // 自行添加
}

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-J50DBr0E-1587955611415)(F:\筆記\Spring\image\構造器自動注入失敗.png)]

有問題可加QQ羣一起交流:1076386005

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章