亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

實(shí)例講解Java的Spring框架中的控制反轉(zhuǎn)和依賴注入

 更新時(shí)間:2016年02月16日 08:55:40   作者:haolloyin  
這篇文章主要介紹了Java的Spring框架中的控制反轉(zhuǎn)和依賴注入,Spring是Java的SSH三大web開發(fā)框架之一,需要的朋友可以參考下

近來總是接觸到 IoC(Inversion of Control,控制反轉(zhuǎn))、DI(Dependency Injection,依賴注入)等編程原則或者模式,而這些是著名 Java 框架 Spring、Struts 等的核心所在。針對(duì)此查了 Wikipedia 中各個(gè)條目,并從圖書館借來相關(guān)書籍,閱讀后有些理解,現(xiàn)結(jié)合書中的講解以及自己的加工整理如下:
 

eg1
問題描述:
開發(fā)一個(gè)能夠按照不同要求生成Excel或 PDF 格式的報(bào)表的系統(tǒng),例如日?qǐng)?bào)表、月報(bào)表等等。
 
解決方案:
根據(jù)“面向接口編程”的原則,應(yīng)該分離接口與實(shí)現(xiàn),即將生成報(bào)表的功能提取為一個(gè)通用接口ReportGenerator,并提供生成 Excel 和 PDF格式報(bào)表的兩個(gè)實(shí)現(xiàn)類 ExcelGenerator 和 PDFGenerator,而客戶Client 再通過服務(wù)提供者 ReportService 獲取相應(yīng)的報(bào)表打印功能。
 
實(shí)現(xiàn)方法:
根據(jù)上面所述,得到如下類圖:

201621685108610.jpg (523×261)

代碼實(shí)現(xiàn):
 

interface ReportGenerator { 
 public void generate(Table table); 
} 
 
class ExcelGenerator implements ReportGenerator { 
 public void generate(Table table) { 
  System.out.println("generate an Excel report ..."); 
 } 
} 
 
class PDFGenerator implements ReportGenerator { 
 public void generate(Table table) { 
  System.out.println("generate an PDF report ..."); 
 } 
} 
 
class ReportService { 
 // 負(fù)責(zé)創(chuàng)建具體需要的報(bào)表生成器 
 private ReportGenerator generator = new PDFGenerator(); 
 // private static ReportGenerator generator = new ExcelGenerator(); 
  
 public void getDailyReport(Date date) { 
  table.setDate(date); 
  // ... 
  generator.generate(table); 
 } 
  
 public void getMonthlyReport(Month month) { 
  table.setMonth(month); 
  // ... 
  generator.generate(table); 
 } 
} 
 
 
public class Client { 
 public static void main(String[] args) { 
  ReportService reportService = new ReportService(); 
  reportService.getDailyReport(new Date()); 
  //reportService.getMonthlyReport(new Date()); 
 } 
} 

 
eg2 
問題描述:
如上面代碼中的注釋所示,具體的報(bào)表生成器由 ReportService 類內(nèi)部硬編碼創(chuàng)建,由此 ReportService 已經(jīng)直接依賴于 PDFGenerator 或 ExcelGenerator ,必須消除這一明顯的緊耦合關(guān)系。
 
解決方案:引入容器
引入一個(gè)中間管理者,也就是容器(Container),由其統(tǒng)一管理報(bào)表系統(tǒng)所涉及的對(duì)象(在這里是組件,我們將其稱為 Bean),包括 ReportService 和各個(gè) XXGenerator 。在這里使用一個(gè)鍵-值對(duì)形式的 HashMap 實(shí)例來保存這些 Bean。
 
實(shí)現(xiàn)方法:
得到類圖如下:

201621685213842.jpg (469×349)

代碼實(shí)現(xiàn):

class Container { 
 // 以鍵-值對(duì)形式保存各種所需組件 Bean 
 private static Map<String, Object> beans; 
  
 public Container() { 
  beans = new HashMap<String, Object>(); 
   
  // 創(chuàng)建、保存具體的報(bào)表生起器 
  ReportGenerator reportGenerator = new PDFGenerator(); 
  beans.put("reportGenerator", reportGenerator); 
   
  // 獲取、管理 ReportService 的引用 
  ReportService reportService = new ReportService(); 
  beans.put("reportService", reportService); 
 } 
  
 public static Object getBean(String id) { 
  return beans.get(id); 
 } 
} 
 
class ReportService { 
 // 消除緊耦合關(guān)系,由容器取而代之 
 // private static ReportGenerator generator = new PDFGenerator(); 
 private ReportGenerator generator = (ReportGenerator) Container.getBean("reportGenerator"); 
 
 public void getDailyReport(Date date) { 
  table.setDate(date); 
  generator.generate(table); 
 } 
  
 public void getMonthlyReport(Month month) { 
  table.setMonth(month); 
  generator.generate(table); 
 } 
} 
 
public class Client { 
 public static void main(String[] args) { 
  Container container = new Container(); 
  ReportService reportService = (ReportService)Container.getBean("reportService"); 
  reportService.getDailyReport(new Date()); 
  //reportService.getMonthlyReport(new Date()); 
 } 
} 

 
時(shí)序圖大致如下:

201621685245412.jpg (594×332)

效果:
如上面所示,ReportService 不再與具體的 ReportGenerator 直接關(guān)聯(lián),已經(jīng)用容器將接口和實(shí)現(xiàn)隔離開來了,提高了系統(tǒng)組件 Bean 的重用性,此時(shí)還可以使用配置文件在 Container 中實(shí)時(shí)獲取具體組件的定義。
 
eg3
問題描述:
然而,觀察上面的類圖,很容易發(fā)現(xiàn) ReportService 與 Container 之間存在雙向關(guān)聯(lián),彼此互相有依賴關(guān)系。并且,如果想要重用 ReportService,由于它也是直接依賴于單獨(dú)一個(gè) Container 的具體查找邏輯。若其他容器具體不同的組件查找機(jī)制(如 JNDI),此時(shí)重用 ReportService 意味著需要修改 Container 的內(nèi)部查找邏輯。
 
解決方案:引入 Service Locator
再次引入一個(gè)間接層 Service Locator,用于提供組件查找邏輯的接口,請(qǐng)看Wikipedia 中的描述 或者 Java EE 對(duì)其的描述1 、描述2 。這樣就能夠?qū)⒖赡茏兓狞c(diǎn)隔離開來。
 
實(shí)現(xiàn)方法:
類圖如下:

201621685311805.jpg (586×345)

代碼實(shí)現(xiàn):
 

// 實(shí)際應(yīng)用中可以是用 interface 來提供統(tǒng)一接口 
class ServiceLocator { 
 private static Container container = new Container(); 
  
 public static ReportGenerator getReportGenerator() { 
  return (ReportGenerator)container.getBean("reportGeneraator"); 
 } 
} 
 
class ReportService { 
 private ReportGenerator reportGenerator = ServiceLocator.getReportGenerator(); 
  
 // ... 
} 


eg4

問題描述:
然而,不管是引入 Container 還是使用 Service Locator ,ReportService 對(duì)于具體組件的查找、創(chuàng)建的方式都是‘主動(dòng)'的,這意味著作為客戶的 ReportService 必須清楚自己需要的是什么、到哪里獲取、如何獲取。一下子就因?yàn)?What、Where、How 而不得不增加了具體邏輯細(xì)節(jié)。
 
例如,在前面‘引入Container '的實(shí)現(xiàn)方法中,有如下代碼: 

class ReportService {
 // 消除緊耦合關(guān)系,由容器取而代之
 // private static ReportGenerator generator = new PDFGenerator();
 // 通過 Container..getBean("reportGenerator") ‘主動(dòng)'查找
 private ReportGenerator generator = (ReportGenerator) Container
   .getBean("reportGenerator");

 
在‘引入 Service Locator '的實(shí)現(xiàn)方法中,有如下代碼: 

class ServiceLocator {
 privatestatic Container container = new Container(); 
 publicstatic ReportGenerator getReportGenerator() {
  // 還是container.getBean(), 用了委托而已
  return (ReportGenerator) container.getBean("reportGeneraator");
 }
} 
class ReportService {
 // ReportService 最終還是‘主動(dòng)'查找,委托給ServiceLocator 而已
 private ReportGenerator reportGenerator = ServiceLocator.getReportGenerator(); 
}

 
解決方案:
在這種情況下,變‘主動(dòng)'為‘被動(dòng)'無疑能夠減少 ReportService 的內(nèi)部知識(shí)(即查找組件的邏輯)。根據(jù)控制反轉(zhuǎn)(IoC)原則,可江此種拉(Pull,主動(dòng)的)轉(zhuǎn)化成推(Push,被動(dòng)的)的模式。
 
例如,平時(shí)使用的 RSS 訂閱就是Push的應(yīng)用,省去了我們一天好幾次登錄自己喜愛的站點(diǎn)主動(dòng)獲取文章更新的麻煩。
 
而依賴注入(DI)則是實(shí)現(xiàn)這種被動(dòng)接收、減少客戶(在這里即ReportService)自身包含復(fù)雜邏輯、知曉過多的弊病。
 
實(shí)現(xiàn)方法:
因?yàn)槲覀兿M恰粍?dòng)'的接收,故還是回到 Container 的例子,而不使用 Service Locator 模式。由此得到修改后的類圖如下:

201621685420071.jpg (572×349)

而原來的類圖如下,可以對(duì)照著看一下,注意注釋的提示:

201621685445066.jpg (578×349)

代碼實(shí)現(xiàn):
為了使例子能夠編譯、運(yùn)行,并且稍微利用跟蹤代碼的運(yùn)行結(jié)果來顯式整個(gè)類圖實(shí)例化、互相協(xié)作的先后順序,在各個(gè)類的構(gòu)造器中加入了不少已編號(hào)的打印語句,以及兩個(gè)無關(guān)緊要的類,有點(diǎn)啰唆,具體如下: 

import java.util.Date;
import java.util.HashMap;
import java.util.Map; 
// 為了能夠編譯運(yùn)行,多了兩個(gè)無關(guān)緊要的類
class Month { }
class Table {
 publicvoid setDate(Date date) { }
 publicvoid setMonth(Month month) { }
} 
// ------------ 以下均無甚重要改變 ----------------- //
interface ReportGenerator {
 publicvoid generate(Table table);
} 
class ExcelGenerator implements ReportGenerator { 
 public ExcelGenerator() {
  System.out.println("2...開始初始化 ExcelGenerator ...");
 } 
 publicvoid generate(Table table) {
  System.out.println("generate an Excel report ...");
 }
} 
class PDFGenerator implements ReportGenerator { 
 public PDFGenerator() {
  System.out.println("2...開始初始化 PDFGenerator ...");
 } 
 publicvoid generate(Table table) {
  System.out.println("generate an PDF report ...");
 }
}
//------------ 以上均無甚重要改變 ----------------- // 
class Container {
 // 以鍵-值對(duì)形式保存各種所需組件 Bean
 privatestatic Map<String, Object> beans; 
 public Container() {
  System.out.println("1...開始初始化 Container ..."); 
  beans = new HashMap<String, Object>(); 
  // 創(chuàng)建、保存具體的報(bào)表生起器
  ReportGenerator reportGenerator = new PDFGenerator();
  beans.put("reportGenerator", reportGenerator); 
  // 獲取、管理 ReportService 的引用
  ReportService reportService = new ReportService();
  // 注入上面已創(chuàng)建的具體 ReportGenerator 實(shí)例
  reportService.setReportGenerator(reportGenerator);
  beans.put("reportService", reportService); 
  System.out.println("5...結(jié)束初始化 Container ...");
 } 
 publicstatic Object getBean(String id) {
  System.out.println("最后獲取服務(wù)組件...getBean() --> " + id + " ...");
  returnbeans.get(id);
 }
}
 
class ReportService { 
 // private static ReportGenerator generator = new PDFGenerator();
 // 消除上面的緊耦合關(guān)系,由容器取而代之
 // private ReportGenerator generator = (ReportGenerator) Container
 //   .getBean("reportGenerator"); 
 // 去除上面的“主動(dòng)”查找,提供私有字段來保存外部注入的對(duì)象
 private ReportGenerator generator; 
 // 以 setter 方式從外部注入
 publicvoid setReportGenerator(ReportGenerator generator) {
  System.out.println("4...開始注入 ReportGenerator ...");
  this.generator = generator;
 }
 
 private Table table = new Table(); 
 public ReportService() {
  System.out.println("3...開始初始化 ReportService ...");
 } 
 publicvoid getDailyReport(Date date) {
  table.setDate(date);
  generator.generate(table);
 } 
 publicvoid getMonthlyReport(Month month) {
  table.setMonth(month);
  generator.generate(table);
 }
}
 
publicclass Client {
 publicstaticvoid main(String[] args) {
  // 初始化容器
  new Container();
  ReportService reportService = (ReportService) Container
    .getBean("reportService");
  reportService.getDailyReport(new Date());
  // reportService.getMonthlyReport(new Date());
 }
}

 
運(yùn)行結(jié)果:

1...開始初始化 Container ...
2...開始初始化 PDFGenerator ...
3...開始初始化 ReportService ...
4...開始注入 ReportGenerator ...
5...結(jié)束初始化 Container ...

最后獲取服務(wù)組件...getBean() --> reportService ...
generate an PDF report ...

 
注意:
1、根據(jù)上面運(yùn)行結(jié)果的打印順序,可見代碼中加入的具體編號(hào)是合理的,模擬了程序執(zhí)行的流程,于是也就不再畫序列圖了。
2、注意該例子中對(duì)IoC、DI的使用,是以ReportService為客戶端(即組件需求者)為基點(diǎn)的,而代碼中的Client 類main()中的測(cè)試代碼才是服務(wù)組件的最終用戶,但它需要的不是組件,而是組件所具有的服務(wù)。
3、實(shí)際在Spring框剪中,初始化Container顯然不是最終用戶Client應(yīng)該做的事情,它應(yīng)該由服務(wù)提供方事先啟動(dòng)就緒。
4、在最終用戶Client中,我們還是用到Container.getBean("reportService")來獲取事先已在Container的構(gòu)造函數(shù)中實(shí)例化好的服務(wù)組件。而在具體應(yīng)用中,通常是用XML等配置文件將可用的服務(wù)組件部署到服務(wù)器中,再由Container讀取該配置文件結(jié)合反射技術(shù)得以創(chuàng)建、注入具體的服務(wù)組件。
 
分析:
之前是由ReportService主動(dòng)從Container中請(qǐng)求獲取服務(wù)組件,而現(xiàn)在是被動(dòng)地等待Container注入(Inject,也就是Push)服務(wù)組件??刂茩?quán)明顯地由底層模塊(ReportService 是組件需求者)轉(zhuǎn)移給高層模塊(Container 是組件提供者),也就是控制反轉(zhuǎn)了。
 

相關(guān)文章

  • SparkSQL使用IDEA快速入門DataFrame與DataSet的完美教程

    SparkSQL使用IDEA快速入門DataFrame與DataSet的完美教程

    本文給大家介紹使用idea開發(fā)Spark SQL 的詳細(xì)過程,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2021-08-08
  • SSM框架下實(shí)現(xiàn)登錄注冊(cè)的示例代碼

    SSM框架下實(shí)現(xiàn)登錄注冊(cè)的示例代碼

    這篇文章主要介紹了SSM框架下實(shí)現(xiàn)登錄注冊(cè)的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-06-06
  • Java全面分析面向?qū)ο笾庋b

    Java全面分析面向?qū)ο笾庋b

    或許大家都聽說過java是純面向?qū)ο笳Z言,面向?qū)ο笏枷胍簿褪俏覀兂Uf的OOP,我們聽說最多的思想就是繼承,封裝,多態(tài),今天我們來講一講封裝
    2022-04-04
  • SpringMVC使用@Valid注解進(jìn)行數(shù)據(jù)驗(yàn)證的方法

    SpringMVC使用@Valid注解進(jìn)行數(shù)據(jù)驗(yàn)證的方法

    本篇文章主要介紹了SpringMVC使用@Valid注解進(jìn)行數(shù)據(jù)驗(yàn)證的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-02-02
  • 如何使用IDEA開發(fā)Spark SQL程序(一文搞懂)

    如何使用IDEA開發(fā)Spark SQL程序(一文搞懂)

    Spark SQL 是一個(gè)用來處理結(jié)構(gòu)化數(shù)據(jù)的spark組件。它提供了一個(gè)叫做DataFrames的可編程抽象數(shù)據(jù)模型,并且可被視為一個(gè)分布式的SQL查詢引擎。這篇文章主要介紹了如何使用IDEA開發(fā)Spark SQL程序(一文搞懂),需要的朋友可以參考下
    2021-08-08
  • springboot整合gateway實(shí)現(xiàn)網(wǎng)關(guān)功能的示例代碼

    springboot整合gateway實(shí)現(xiàn)網(wǎng)關(guān)功能的示例代碼

    本文主要介紹了springboot整合gateway實(shí)現(xiàn)網(wǎng)關(guān)功能的示例代碼,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • SpringBoot集成Quartz實(shí)現(xiàn)持久化定時(shí)接口調(diào)用任務(wù)

    SpringBoot集成Quartz實(shí)現(xiàn)持久化定時(shí)接口調(diào)用任務(wù)

    Quartz是功能強(qiáng)大的開源作業(yè)調(diào)度庫,幾乎可以集成到任何?Java?應(yīng)用程序中,從最小的獨(dú)立應(yīng)用程序到最大的電子商務(wù)系統(tǒng),本文將通過代碼示例給大家介紹SpringBoot集成Quartz實(shí)現(xiàn)持久化定時(shí)接口調(diào)用任務(wù),需要的朋友可以參考下
    2023-07-07
  • 解決程序包javafx.scene.media不存在的問題

    解決程序包javafx.scene.media不存在的問題

    在pom.xml中定位依賴標(biāo)簽,下方添加新依賴時(shí)注意版本號(hào)與項(xiàng)目一致,僅修改紅色部分內(nèi)容,保存后重啟IDEA以刷新配置,確保依賴正確加載
    2025-09-09
  • 分析ThreadLocal內(nèi)存泄漏問題

    分析ThreadLocal內(nèi)存泄漏問題

    ThreadLocal的作用是提供線程內(nèi)的局部變量,這種變量在線程生命周期內(nèi)起作用,減少同一個(gè)線程內(nèi)多個(gè)函數(shù)或者組件之間一些公共變量傳遞的復(fù)雜度,但是如果濫用ThreadLocal可能會(huì)導(dǎo)致內(nèi)存泄漏,所以本文將為大家分析ThreadLocal內(nèi)存泄漏問題
    2023-07-07
  • nexus安裝及配置圖文教程

    nexus安裝及配置圖文教程

    Nexus 是Maven倉庫管理器,通過nexus可以搭建maven倉庫,同時(shí)nexus還提供強(qiáng)大的倉庫管理功能,構(gòu)件搜索功能等,文中有非常詳細(xì)的圖文介紹,對(duì)小伙伴們很有幫助,需要的朋友可以參考下
    2021-05-05

最新評(píng)論