當(dāng)前位置:首頁(yè) > IT技術(shù) > 編程語(yǔ)言 > 正文

spring框架-認(rèn)識(shí)IOC(二)
2021-12-13 17:52:03

??

IOC是什么

IoC(Inversion of Control)控制反轉(zhuǎn),包含了兩個(gè)方面:一、控制。二、反轉(zhuǎn)

類與類依賴關(guān)系交給容器處理。IoC不是一種技術(shù),只是一種思想,一個(gè)重要的面向?qū)ο缶幊痰姆▌t,它能指導(dǎo)我們?nèi)绾卧O(shè)計(jì)出松耦合、更優(yōu)良的程序。

IOC不夠開(kāi)門見(jiàn)山,于是Martin Fowler提出了DI(dependency injection)依賴注入來(lái)替代IoC,即讓調(diào)用類對(duì)某一接口實(shí)現(xiàn)類的依賴關(guān)系由第三方(容器或協(xié)作類)注入,以移除調(diào)用類對(duì)某一接口實(shí)現(xiàn)類的依賴。

IOC有兩種方式:DI(依賴注入)和DL (依賴查找)


IOC的優(yōu)點(diǎn)


  1. ?減少了對(duì)象的創(chuàng)建和管理 ,使代碼層次更加清晰。
  2. Spring 的IOC容器是一個(gè)輕量級(jí)的容器 ,沒(méi)有侵入性(不依賴容器的API) ,不需要實(shí)現(xiàn)一些特殊接口。
  3. 鼓勵(lì)我們面向接口編程。
  4. 減少了代碼的耦合,將耦合的部分推到了配置文件中 ,如果他們的關(guān)系發(fā)生了改變,只需要修改配置文件。


DL (依賴查找)

程序提供查找方式,交給容器去查找(回調(diào)函數(shù))

容器提供回調(diào)接口和上下文環(huán)境給組件。EJB和Apache Avalon都使用這種方式

下面代碼展示了基于JNDI實(shí)現(xiàn)的依賴查找機(jī)制。

 public class MyBusniessObject{
private DataSource ds;
private MyCollaborator myCollaborator;

public MyBusnissObject(){
Context ctx = null;
try{
ctx = new InitialContext();
ds = (DataSource) ctx.lookup(“java:comp/env/dataSourceName”);
myCollaborator =
(MyCollaborator) ctx.lookup(“java:comp/env/myCollaboratorName”);
}……

但是日常開(kāi)發(fā)中,EJB類似的已經(jīng)很少用到了,所以很多同學(xué)沒(méi)聽(tīng)過(guò)DL(依賴查找),這很正常,大家更熟悉的是DI(依賴注入)。

不過(guò)這兩個(gè)查找大家應(yīng)該用過(guò):


  1. 名稱查找 - autowireByName
  2. 類型查找 - autowireByType

名稱查找 - autowireByName

直接從 BeanFactory 中取出這個(gè) bean 就可以了,常用的就是@Qualifier?

類型查找 - autowireByType

常用的就是@autowire

如果容器中存在一個(gè)與指定屬性類型相同的bean,那么將與該屬性自動(dòng)裝配。如果存在多個(gè)該類型的bean,那么將會(huì)拋出異常

簡(jiǎn)單的理解就是通過(guò)類名去匹配


?DI(依賴注入)

一個(gè)對(duì)象需要另外一個(gè)對(duì)象時(shí),無(wú)需在代碼中創(chuàng)建被調(diào)用者,而是依賴于外部容器,由外部容器創(chuàng)建后傳遞給程序

用圖例說(shuō)明一下,傳統(tǒng)程序設(shè)計(jì)如下圖1,都是主動(dòng)去創(chuàng)建相關(guān)對(duì)象然后再組合起來(lái):



spring框架-認(rèn)識(shí)IOC(二)_spring

圖1


當(dāng)有了IoC/DI的容器后,在客戶端類中不再主動(dòng)去創(chuàng)建這些對(duì)象了,如圖2所示



spring框架-認(rèn)識(shí)IOC(二)_spring_02

圖2



IOC容器

IOC容器其實(shí)就是一個(gè)大工廠,它用來(lái)管理我們所有的對(duì)象以及依賴關(guān)系。


  • 原理就是通過(guò)Java的反射技術(shù)來(lái)實(shí)現(xiàn)的!通過(guò)反射我們可以獲取類的所有信息(成員變量、類名等等等)!
  • 再通過(guò)配置文件(xml)或者注解來(lái)描述類與類之間的關(guān)系
  • 我們就可以通過(guò)這些配置信息和反射技術(shù)來(lái)構(gòu)建出對(duì)應(yīng)的對(duì)象和依賴關(guān)系了!

Spring容器(Bean工廠)?


  1. BeanFactory:這是最基礎(chǔ)、面向Spring的
  2. ApplicationContext:這是在BeanFactory基礎(chǔ)之上,面向使用Spring框架的開(kāi)發(fā)者。提供了一系列的功能!

ApplicationContext這個(gè)大家就很熟悉了吧,spring絕大部分應(yīng)用都是使用ApplicationContext

BeanFactory和ApplicationContext區(qū)別

BeanFactory?可以理解為含有bean集合的工廠類。BeanFactory?包含了種bean的定義,以便在接收到客戶端請(qǐng)求時(shí)將對(duì)應(yīng)的bean實(shí)例化。

BeanFactory還能在實(shí)例化對(duì)象的時(shí)生成協(xié)作類之間的關(guān)系。此舉將bean自身與bean客戶端的配置中解放出來(lái)。BeanFactory還包含了bean生命周期的控制,調(diào)用客戶端的初始化方法(initialization methods)和銷毀方法(destruction methods)。

applicationcontext是beanFactory的子接口,擁有BeanFactory的所有功能,但applicationcontext在此基礎(chǔ)上還提供了其他的功能。


  1. 提供了支持國(guó)際化的文本消息
  2. 統(tǒng)一的資源文件讀取方式
  3. 已在監(jiān)聽(tīng)器中注冊(cè)的bean的事件
    且beanFactory是延遲加載,需要類的時(shí)候才創(chuàng)建類的實(shí)例,而ApplicationContext在初始化時(shí)就加載完成了所有的單例bean

以下是三種較常見(jiàn)的?ApplicationContext?實(shí)現(xiàn)方式:

1、ClassPathXmlApplicationContext:從classpath的XML配置文件中讀取上下文,并生成上下文定義。應(yīng)用程序上下文從程序環(huán)境變量中取得

    ApplicationContext context = new ClassPathXmlApplicationContext(“bean.xml”);  

2、FileSystemXmlApplicationContext :由文件系統(tǒng)中的XML配置文件讀取上下文。

??ApplicationContext context = new FileSystemXmlApplicationContext(“bean.xml”);???

3、XmlWebApplicationContext:由Web應(yīng)用的XML文件讀取上下文。

Spring Bean的生命周期?

Spring Bean的生命周期簡(jiǎn)單易懂。在一個(gè)bean實(shí)例被初始化時(shí),需要執(zhí)行一系列的初始化操作以達(dá)到可用的狀態(tài)。同樣的,當(dāng)一個(gè)bean不在被調(diào)用時(shí)需要進(jìn)行相關(guān)的析構(gòu)操作,并從bean容器中移除。

Spring bean factory 負(fù)責(zé)管理在spring容器中被創(chuàng)建的bean的生命周期。Bean的生命周期由兩組回調(diào)(call back)方法組成。


  1. 初始化之后調(diào)用的回調(diào)方法。
  2. 銷毀之前調(diào)用的回調(diào)方法。

Spring框架提供了以下四種方式來(lái)管理bean的生命周期事件:


  • InitializingBean和DisposableBean回調(diào)接口
  • 針對(duì)特殊行為的其他Aware接口
  • Bean配置文件中的Custom init()方法和destroy()方法
  • @PostConstruct和@PreDestroy注解方式 使用??customInit()??和???customDestroy()????方法管理????bean????生命周期的代碼樣例如下:????<beans> <bean id="demoBean" class="com.somnus.task.DemoBean" init-method="customInit" destroy-method="customDestroy"> </bean> </beans> ???


?裝配Bean方式?

Spring4.x開(kāi)始IOC容器裝配Bean有4種方式:


  1. XML配置
  2. 注解
  3. JavaConfig
  4. 基于Groovy DSL配置(這種很少見(jiàn))

日常開(kāi)發(fā)中,常用到的是XML配置+注解。

剩下的兩種有興趣的可以自行百度+google


依賴注入方式

依賴注入的方式有3種方式:


  1. 屬性注入-->通過(guò)??setter()??方法注入
  2. 構(gòu)造方法注入
  3. 工廠方法注入

構(gòu)造方法注入和屬性注入有什么區(qū)別?


  1. 在屬性注入方法支持大部分的依賴注入,如果我們僅需要注入int、string和long型的變量,我們不要用設(shè)值的方法注入。對(duì)于基本類型,如果我們沒(méi)有注入的話,可以為基本類型設(shè)置默認(rèn)值。在構(gòu)造方法注入不支持大部分的依賴注入,因?yàn)樵谡{(diào)用構(gòu)造方法中必須傳入正確的構(gòu)造參數(shù),否則的話為報(bào)錯(cuò)。
  2. 屬性注入不會(huì)重寫構(gòu)造方法的值。如果我們對(duì)同一個(gè)變量同時(shí)使用了構(gòu)造方法注入又使用了設(shè)置方法注入的話,那么構(gòu)造方法將不能覆蓋由設(shè)值方法注入的值。很明顯,因?yàn)闃?gòu)造方法盡在對(duì)象被創(chuàng)建時(shí)調(diào)用。
  3. 在使用屬性注入時(shí)有可能還不能保證某種依賴是否已經(jīng)被注入,也就是說(shuō)這時(shí)對(duì)象的依賴關(guān)系有可能是不完整的。而在另一種情況下,構(gòu)造器注入則不允許生成依賴關(guān)系不完整的對(duì)象。
  4. 在屬性注入時(shí)如果對(duì)象A和對(duì)象B互相依賴,在創(chuàng)建對(duì)象A時(shí)Spring會(huì)拋出s??ObjectCurrentlyInCreationException異常,因?yàn)樵贐對(duì)象被創(chuàng)建之前A對(duì)象是不能被創(chuàng)建的,反之亦然。所以Spring用設(shè)值注入的方法解決了循環(huán)依賴的問(wèn)題,因?qū)ο蟮脑O(shè)值方法是在對(duì)象被創(chuàng)建之前被調(diào)用的。??


Bean的作用域

Spring容器中的bean可以分為5個(gè)范圍。所有范圍的名稱都是自說(shuō)明的,但是為了避免混淆,還是讓我們來(lái)解釋一下:

使用3,4,5作用域的,需要手動(dòng)設(shè)置代理


  1. singleton:這種bean范圍是默認(rèn)的,這種范圍確保不管接受到多少個(gè)請(qǐng)求,每個(gè)容器中只有一個(gè)bean的實(shí)例,單例的模式由bean factory自身來(lái)維護(hù)。
  2. prototype:多例范圍與單例范圍相反,為每一個(gè)bean請(qǐng)求提供一個(gè)實(shí)例。
  3. request:在請(qǐng)求bean范圍內(nèi)會(huì)每一個(gè)來(lái)自客戶端的網(wǎng)絡(luò)請(qǐng)求創(chuàng)建一個(gè)實(shí)例,在請(qǐng)求完成以后,bean會(huì)失效并被垃圾回收器回收。
  4. Session:與請(qǐng)求范圍類似,確保每個(gè)session中有一個(gè)bean的實(shí)例,在session過(guò)期后,bean會(huì)隨之失效。
  5. global-session:global-session和Portlet應(yīng)用相關(guān)。當(dāng)你的應(yīng)用部署在Portlet容器中工作時(shí),它包含很多portlet。如果你想要聲明讓所有的portlet共用全局的存儲(chǔ)變量的話,那么這全局變量需要存儲(chǔ)在global-session中。全局作用域與Servlet中的session作用域效果相同。


bean的自動(dòng)裝配

使用bean元素的autowire屬性來(lái)指定Bean定義的自動(dòng)裝配,共有5中模式:


  1. no ? 默認(rèn)的方式是不進(jìn)行自動(dòng)裝配,通過(guò)手工設(shè)置ref 屬性來(lái)進(jìn)行裝配bean
  2. byName ? 依賴的 bean 名稱需要與類中引用的名稱一致? ,就會(huì)匹配依賴關(guān)系,我們?cè)陬愔械囊玫拿Q是 userAutowireDao 所以就會(huì)去匹配我們的 userAutowireDao 方法
  3. byType ? 通過(guò)參數(shù)的數(shù)據(jù)類型自動(dòng)自動(dòng)裝配,如果一個(gè)bean的數(shù)據(jù)類型和另外一個(gè)bean的property屬性的數(shù)據(jù)類型兼容,就自動(dòng)裝配,簡(jiǎn)單的理解就是通過(guò)類名去匹配
  4. construct ? 構(gòu)造方法中的參數(shù)通過(guò)byType的形式,自動(dòng)裝配。
  5. default 由上級(jí)標(biāo)簽<beans>的default-autowire屬性確定。
    ?


常用注解詳解

注解注入就是用注解標(biāo)簽的方式來(lái)替換掉我們 xml 配置文件里面 bean 的注冊(cè)和依賴

@Component

用于類上

所有的類上面都可以這么寫,通用注解,這是不規(guī)范的寫法,哈哈哈

spring框架-認(rèn)識(shí)IOC(二)_IOC_03

@Repository

用于類上

這個(gè)注解主要是聲明 dao 的類組件

spring框架-認(rèn)識(shí)IOC(二)_依賴注入_04

@Service?

這個(gè)注解主要是聲明 service 服務(wù)類

spring框架-認(rèn)識(shí)IOC(二)_構(gòu)造方法_05

@Controller

主要是聲明控制類 (springmvc/struts2 action/controller)

spring框架-認(rèn)識(shí)IOC(二)_IOC_06

@Resource?

用于類內(nèi)

javaEE 的注解 ,默認(rèn)是以 byName 方式注入,byName 找不到的話,再用 byType 去匹配

效果跟Autowired一樣,查找順序相反

@Resource有兩個(gè)屬性是比較重要的,分是name和type,Spring將@Resource注解的name屬性解析為bean的名字,而type屬性則解析為bean的類型。所以如果使用name屬性,則使用byName的自動(dòng)注入策略,而使用type屬性時(shí)則使用byType自動(dòng)注入策略。如果既不指定name也不指定type屬性,這時(shí)將通過(guò)反射機(jī)制使用byName自動(dòng)注入策略。

spring框架-認(rèn)識(shí)IOC(二)_構(gòu)造方法_07

spring框架-認(rèn)識(shí)IOC(二)_IOC_08

spring框架-認(rèn)識(shí)IOC(二)_構(gòu)造方法_09

@Autowired?

用于類內(nèi)

spring 的注解,默認(rèn)是以 byType 注入,-如果有多個(gè)實(shí)現(xiàn)類,他再用 byName 的方式(@Qualifier)去匹配

效果跟Resource一樣,查找順序相反

Autowired和Qualifier一起用,

eg:

@Autowired

@Qualifier(value = "TestService2")

private TestService testService;

//實(shí)現(xiàn)類

@Service("TestService1")

public class TestServiceImpl implements TestService {...}

//實(shí)現(xiàn)類

@Service("TestService2")

public class TestServiceImpl implements TestService {...}

spring框架-認(rèn)識(shí)IOC(二)_spring_10

?@Qualifier

spring的注解,可以指定實(shí)現(xiàn)的方法名稱

spring框架-認(rèn)識(shí)IOC(二)_構(gòu)造方法_11

@Scope?

bean的作用域,可以查看上面的概念,這里就不再重復(fù)了


總結(jié)

借鑒了其他博主的思路:會(huì)整理出Spring思維導(dǎo)圖出來(lái),等AOP寫好一并放出來(lái)。

今天的spring介紹就寫到這里,再見(jiàn)!

?


本文摘自 :https://blog.51cto.com/u

開(kāi)通會(huì)員,享受整站包年服務(wù)立即開(kāi)通 >