概念和作用
程序耦合:
在軟件工程中,耦合指的就是就是對象之間的依賴性。對象之間的耦合越高,維護成本越高。因此對象的設計應使類和構件之間的耦合最小。軟件設計中通常用耦合度和內聚度作爲衡量模塊獨立程度的標準。劃分模塊的一個準則就是高內聚低耦合。
解決耦合思路:反射 + 配置文件,減少 new
關鍵字的使用
工廠模式解耦:
實際開發中我們可以把三層的對象都使用配置文件配置起來,當啓動服務器應用加載的時候,讓一個類中的方法通過讀取配置文件,把這些對象創建出來並存起來。在接下來的使用的時候,直接拿過來用。這個讀取配置文件,創建和獲取三層對象的類就是工廠。
控制反轉:
問題一:對象存哪 ?
由於我們是很多對象,肯定要找個集合來存,這時候有 Map
和 List
供選擇。由於我們有查找需求,所以選擇 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();
}
}
執行結果
瞭解常用類
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
標籤,配以 id
和 class
屬性之後,且沒有其他屬性和標籤時採用的是默認構造函數創建 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 類型數據,它指的就是在 Spring 的 IOC 核心容器中出現過的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);
Set 方法注入
屬性值 | 作用 |
---|---|
name |
用於指定注入時調用的 set 方法的名稱 |
value |
用於提供基本類型和 String 類型的數據 |
ref |
用於指定其他的 bean 類型數據,指的是在 Spring 的 IOC 核心容器中出現過的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>
證明 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>
情況二: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>
測試 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>
情況二:沒有相對應的構造器(還是使用情況一的配置,只是修改 Man
類)
public class Man {
private Person person;
public Man() {
}
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
toString(); // 自行添加
}
有問題可加QQ羣一起交流:1076386005