1、IoC 依賴注入(控制反轉(zhuǎn)) 方便解耦
耦合:
業(yè)務(wù)層的A類中要使用持久層B類對(duì)象,于是在A類中new了一個(gè)B類對(duì)象來(lái)用,這就導(dǎo)致了A類會(huì)強(qiáng)依賴于B類,產(chǎn)生耦合,耦合只能減弱不能消除,消除相當(dāng)于這兩個(gè)類毫無(wú)關(guān)系了。
減弱耦合:
不在A類中newB類的對(duì)象,建立一個(gè)對(duì)象容器(工廠),這個(gè)容器里面就產(chǎn)生各種對(duì)象給別的類調(diào)用,對(duì)象的生成只在這個(gè)容器中,可以把要生成的對(duì)象path寫在properties或xml文件中,在容器中用靜態(tài)代碼塊讀取properties或xml中的內(nèi)容,通過(guò)反射生成對(duì)象,保存在容器中,比如一個(gè)Map中,再向外界提供一個(gè)根據(jù)map的key獲取對(duì)象的方法。
1、1那么spring是如何做到減弱耦合的呢?
原理和上面的對(duì)象容器工廠差不多,容器由spring完成了,我們只需要在xml中配置業(yè)務(wù)層和持久層的對(duì)象信息:說(shuō)明要?jiǎng)?chuàng)建哪個(gè)對(duì)象、用什么方式去?。?/p>
<bean id=“accountService” class=“com.demo.service.impl.AccountServiceImpl”><bean/>
<bean id=“accountDao” class=“com.demo.dao.impl.AccountDaoImpl”><bean/>
id:對(duì)象的唯一標(biāo)示;class:要?jiǎng)?chuàng)建對(duì)象的全限定類名
spring有一個(gè)核心容器,xml中讀取的對(duì)象都生成放在核心容器中;下面我們只需要獲取到核心容器后,再根據(jù)bean的id獲取對(duì)象:
核心容器類:
ApplicationContext:
是BeanFactory下的子接口的再子接口。
特點(diǎn)是創(chuàng)建bean對(duì)象時(shí),采用的是立即加載的策略。(當(dāng)讀取完xml配置文件,配置文件中所有的bean對(duì)象都已經(jīng)創(chuàng)建完成了,實(shí)測(cè))
BeanFactory:
它是springioc容器的頂層接口。
特點(diǎn)是創(chuàng)建bean對(duì)象時(shí),采用的是延遲加載的策略。(當(dāng)真正要從容器獲取bean對(duì)象時(shí)才創(chuàng)建,讀完配置文件時(shí)不創(chuàng)建)
單例對(duì)象可以用ApplicationContext,多例對(duì)象用BeanFactory,不過(guò)spring會(huì)幫我們處理,一般都用ApplicationContext,設(shè)置屬性可以選擇單例多例。
- 用bean標(biāo)簽創(chuàng)建bean對(duì)象三種創(chuàng)建方式:
1、通過(guò)調(diào)用默認(rèn)構(gòu)造函數(shù)來(lái)創(chuàng)建bean對(duì)象,默認(rèn)情況下,在bean標(biāo)簽中寫了class屬性,spring就會(huì)調(diào)用默認(rèn)構(gòu)造函數(shù)創(chuàng)建對(duì)象,如果沒(méi)有默認(rèn)構(gòu)造函數(shù),則對(duì)象創(chuàng)建失敗:
<bean id=“唯一標(biāo)示” class=“全限定類名”></bean>
2、靜態(tài)工廠創(chuàng)建,項(xiàng)目中已經(jīng)有一個(gè)創(chuàng)建對(duì)象的工廠類,其中有創(chuàng)建對(duì)象的靜態(tài)方法可以使用:
<bean id=“唯一標(biāo)示” class=“全限定類名(工廠類)” factory-method=“方法名”></bean>
3、實(shí)例工廠創(chuàng)建,項(xiàng)目中已經(jīng)有一個(gè)創(chuàng)建對(duì)象的工廠類,其中有創(chuàng)建對(duì)象的非靜態(tài)方法可以使用:
<bean id=“唯一標(biāo)示” factory-bean=“工廠bean的id” factory-method=“方法名”></bean>
bean對(duì)象的作用范圍:
可以通過(guò)一個(gè)配置屬性來(lái)指定:scope屬性: 比如 scope=“singleton”
有幾個(gè)值:
singleton:?jiǎn)卫?(默認(rèn)值,也最常用)
prototype:多例的
request:請(qǐng)求范圍
session:會(huì)話范圍
global-session:全局會(huì)話范圍Bean對(duì)象的生命周期:
單例對(duì)象
出生:容器創(chuàng)建,對(duì)象出生
活著:只要容器在,對(duì)象就一直可用
死亡:容器銷毀,對(duì)象消亡
多例對(duì)象
出生:每次使用時(shí),容器才會(huì)為我們創(chuàng)建對(duì)象
活著:只要對(duì)象在使用過(guò)程中就一直活著
死亡:spring不知道對(duì)象你什么時(shí)候用完,spring把這件事交給了java的gc:對(duì)象長(zhǎng)時(shí)間不用,并且也沒(méi)有其他對(duì)象引用時(shí),java的垃圾回收器回收
1、2對(duì)象創(chuàng)建出來(lái)了,那么依賴關(guān)系要怎么構(gòu)建呢,spring提供了依賴注入:
場(chǎng)景:展示層調(diào)用AccountServiceImpl(業(yè)務(wù)層)的saveAccount方法,這個(gè)方法中又調(diào)用AccountDao(持久層)類的保存賬號(hào)方法,那這個(gè)時(shí)候AccountServiceImpl類中就需要引用到AccountDao對(duì)象,我們前面已經(jīng)把這兩個(gè)對(duì)象都用spring創(chuàng)建出來(lái)了,那么現(xiàn)在開(kāi)始往AccountServiceImpl中依賴AccountDao對(duì)象,為了簡(jiǎn)單我們先用簡(jiǎn)單類型的屬性代替AccountDao對(duì)象引入到AccountServiceImpl中:
1、構(gòu)造函數(shù)注入:
使用constructor-arg標(biāo)簽,該標(biāo)簽是寫在bean標(biāo)簽內(nèi)部的子標(biāo)簽:
標(biāo)簽的屬性:
type:指定要注入的參數(shù)在構(gòu)造函數(shù)中的類型
index:指定要注入的參數(shù)在構(gòu)造函數(shù)中的索引位置
name:指定要注入的參數(shù)在構(gòu)造函數(shù)中的名稱
value:指定要注入數(shù)據(jù)內(nèi)容,他只能指定基本類型數(shù)據(jù)和String類型數(shù)據(jù)
ref:指定其他bean類型數(shù)據(jù)。寫的是其他bean的id。其他bean指的是存在于spring容器中的bean。
例子:
<bean id=“accountService” class=“com.demo.service.impl.AccountServiceImpl”>
<constructor-arg name=“name” value=“xc”></constructor-org>
<constructor-arg name=“age” value=“27”></constructor-org>
<constructor-arg name=“birthday” ref=“now”></constructor-org>
<bean/>
<bean id=“now” class=“java.util.Date”></bean>
這種方式需要AccountServiceImpl類中有帶有這三個(gè)參數(shù)的構(gòu)造函數(shù),否則報(bào)錯(cuò)。
這時(shí)候在展示層通過(guò)spring獲取的AccountServiceImpl對(duì)象其中的三個(gè)屬性就已經(jīng)有了上面設(shè)置的值,這感覺(jué)有點(diǎn)像是初始化對(duì)象賦值呢?不過(guò)好像確實(shí)是這樣,因?yàn)槭怯脴?gòu)造函數(shù)注入的。
2、使用setter方法注入:(常用)
涉及的標(biāo)簽:property。也是要寫在bean標(biāo)簽內(nèi)部的子標(biāo)簽
標(biāo)簽屬性:
name:指定的是setter方法的名稱。匹配的是類中setter方法set后面的部分:setAppName(),那么這里就取appName,首字母要小寫。
value:指定要注入數(shù)據(jù)內(nèi)容,他只能指定基本類型數(shù)據(jù)和String類型數(shù)據(jù)
ref:指定其他bean類型數(shù)據(jù)。寫的是其他bean的id。其他bean指的是存在于spring容器中的bean。
例子:
<bean id=“accountService” class=“com.demo.service.impl.AccountServiceImpl”>
<property name=“name” value=“xc”></property>
<property name=“age” value=“27”></property>
<property name=“birthday” ref=“now”></property>
<bean/>
這種方式需要類中的屬性有set方法,可以沒(méi)有g(shù)et方法,但是一定要有set方法.
用這種方式往AccountServiceImpl中引用AccountDao:
<bean id=“accountService” class=“com.demo.service.impl.AccountServiceImpl”>
<property name=“accountDao” ref=“accountDao”></constructor-org>
<bean/>
<bean id=“accountDao” class=“com.demo.dao.impl.AccountDaoImpl”><bean/>
這樣寫完,業(yè)務(wù)層和持久層的依賴關(guān)系就交給了spring來(lái)維護(hù)了。
- 2.1使用p名稱空間注入:它的本質(zhì)仍然是需要類中提供set方法,同時(shí)在配置文件中要導(dǎo)入p名稱空間:
<bean id=“accountService” class=“com.demo.service.impl.AccountServiceImpl” p:name=“張三”>
<bean/>
- 2.2復(fù)雜類型注入:注入的方式采用set方法,注入的類型都是集合類型:
注意:結(jié)構(gòu)相同標(biāo)簽可以互換<!— 給數(shù)組注入數(shù)據(jù)—> <property name=“myStrs” > <array> <value>AAA</value> <value>BBB</value> <value>ccc</value> </array> </property> <!— 給list注入數(shù)據(jù)—> <property name=“myList” > <list> <value>AAA</value> <value>BBB</value> <value>ccc</value> </list> </property> <!— 給set注入數(shù)據(jù)—> <property name=“mySet” > <set> <value>AAA</value> <value>BBB</value> <value>ccc</value> </set> </property> <!— 給map注入數(shù)據(jù)—> <property name=“myMap” > <map> <entry key=“testA” value=“AAA”></value> <entry key=“testB” value=“BBB”></value> </map> </property> <!— 給properties注入數(shù)據(jù)—> <property name=“myProps” > <props> <prop key=“testa”>AAA</value> <prop key=“testB”>BBB</value> </props> </property> <bean/>
1.3用spring注解的方式再做一遍上面的工作:
和上面xml的目的都是為了降低耦合,只不過(guò)是形式不一樣
* 用于創(chuàng)建對(duì)象的:
*
* @Component :
* 作用:就相當(dāng)于在spring的xml配置文件中寫了一個(gè)bean標(biāo)簽。
* 屬性:
* value:用于指定bean的Id。當(dāng)不寫時(shí),默認(rèn)值是當(dāng)前類名,首字母改小寫。
* 由此注解衍生的三個(gè)注解:
* @Controller, 用于表現(xiàn)層
* @Service, 用于業(yè)務(wù)層
* @Repository 用于持久層
* 他們的作用以及屬性和@Component的作用是一樣的。它們的出現(xiàn)時(shí)spring框架為我們提供更明確的語(yǔ)義話來(lái)指定不同層的bean對(duì)象。
* <p>
* 用于注入數(shù)據(jù)的:
* @Autowired
* 作用:自動(dòng)按照類型注入。只要容器中有唯一的類型匹配,則可以直接注入成功。
* 細(xì)節(jié):當(dāng)使用此注解注入時(shí),set方法就可以省略了。
* 屬性:required :是否必須注入成功。取值true(默認(rèn)值)/false。當(dāng)取值是true時(shí),沒(méi)有匹配的對(duì)象就會(huì)報(bào)錯(cuò)。寫不寫無(wú)所謂,反正沒(méi)有匹配到總會(huì)報(bào)錯(cuò),只不過(guò)報(bào)錯(cuò)信息不一樣。
* 用于改變作用范圍的:
* 和生命周期相關(guān)的:
*/
@Component(value = "accountService")
public class AccountServiceImpl implements IAccountService {
@Autowired(required = false)
IAccountDao accountDao;
@Override
public void saveAccount() {
accountDao.saveAccount();
}
}
public class Client {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
IAccountService accountService = ac.getBean("accountService",IAccountService.class);
IAccountDao accountDao = ac.getBean("accountDao",IAccountDao.class);
accountService.saveAccount();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!--告知spring創(chuàng)建容器時(shí)要掃描的包-->
<context:component-scan base-package="com.ncz.nczupkeep.biz"></context:component-scan>
</beans>