国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Java > 正文

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

2019-11-26 14:34:03
字體:
供稿:網(wǎng)友

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

eg1
問題描述:
開發(fā)一個(gè)能夠按照不同要求生成Excel或 PDF 格式的報(bào)表的系統(tǒ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)所涉及的對象(在這里是組件,我們將其稱為 Bean),包括 ReportService 和各個(gè) XXGenerator 。在這里使用一個(gè)鍵-值對形式的 HashMap 實(shí)例來保存這些 Bean。
 
實(shí)現(xiàn)方法:
得到類圖如下:

201621685213842.jpg (469×349)

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

class Container {  // 以鍵-值對形式保存各種所需組件 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,用于提供組件查找邏輯的接口,請看Wikipedia 中的描述 或者 Java EE 對其的描述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 對于具體組件的查找、創(chuà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") ‘主動'查找 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 最終還是‘主動'查找,委托給ServiceLocator 而已 private ReportGenerator reportGenerator = ServiceLocator.getReportGenerator(); }

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

201621685420071.jpg (572×349)

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

201621685445066.jpg (578×349)

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

主站蜘蛛池模板: 葫芦岛市| 和林格尔县| 桦甸市| 葵青区| 潮安县| 雅安市| 洪江市| 饶阳县| 海口市| 石泉县| 阳朔县| 平山县| 彭州市| 锦屏县| 阿勒泰市| 清水河县| 新泰市| 龙井市| 西华县| 会理县| 元阳县| 杭州市| 康马县| 肥乡县| 临沭县| 盐边县| 大新县| 鄂州市| 慈利县| 曲松县| 偏关县| 抚松县| 阿拉善右旗| 望谟县| 沐川县| 平凉市| 招远市| 元氏县| 安图县| 阳高县| 明溪县|