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

spring框架-認識IOC(二)
2021-12-13 17:52:03

??

IOC是什么

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

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

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

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


IOC的優(yōu)點


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


DL (依賴查找)

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

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

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

 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”);
}……

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

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


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

名稱查找 - autowireByName

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

類型查找 - autowireByType

常用的就是@autowire

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

簡單的理解就是通過類名去匹配


?DI(依賴注入)

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

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



spring框架-認識IOC(二)_spring

圖1


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



spring框架-認識IOC(二)_spring_02

圖2



IOC容器

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


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

Spring容器(Bean工廠)?


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

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

BeanFactory和ApplicationContext區(qū)別

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

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

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


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

以下是三種較常見的?ApplicationContext?實現(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的生命周期簡單易懂。在一個bean實例被初始化時,需要執(zhí)行一系列的初始化操作以達到可用的狀態(tài)。同樣的,當(dāng)一個bean不在被調(diào)用時需要進行相關(guān)的析構(gòu)操作,并從bean容器中移除。

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


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

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


  • InitializingBean和DisposableBean回調(diào)接口
  • 針對特殊行為的其他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開始IOC容器裝配Bean有4種方式:


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

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

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


依賴注入方式

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


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

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


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


Bean的作用域

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

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


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


bean的自動裝配

使用bean元素的autowire屬性來指定Bean定義的自動裝配,共有5中模式:


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


常用注解詳解

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

@Component

用于類上

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

spring框架-認識IOC(二)_IOC_03

@Repository

用于類上

這個注解主要是聲明 dao 的類組件

spring框架-認識IOC(二)_依賴注入_04

@Service?

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

spring框架-認識IOC(二)_構(gòu)造方法_05

@Controller

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

spring框架-認識IOC(二)_IOC_06

@Resource?

用于類內(nèi)

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

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

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

spring框架-認識IOC(二)_構(gòu)造方法_07

spring框架-認識IOC(二)_IOC_08

spring框架-認識IOC(二)_構(gòu)造方法_09

@Autowired?

用于類內(nèi)

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

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

Autowired和Qualifier一起用,

eg:

@Autowired

@Qualifier(value = "TestService2")

private TestService testService;

//實現(xiàn)類

@Service("TestService1")

public class TestServiceImpl implements TestService {...}

//實現(xiàn)類

@Service("TestService2")

public class TestServiceImpl implements TestService {...}

spring框架-認識IOC(二)_spring_10

?@Qualifier

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

spring框架-認識IOC(二)_構(gòu)造方法_11

@Scope?

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


總結(jié)

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

今天的spring介紹就寫到這里,再見!

?


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

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