實例講授Java的Spring框架中的掌握反轉和依附注入。本站提示廣大學習愛好者:(實例講授Java的Spring框架中的掌握反轉和依附注入)文章只能為提供參考,不一定能成為您想要的結果。以下是實例講授Java的Spring框架中的掌握反轉和依附注入正文
最近老是接觸到 IoC(Inversion of Control,掌握反轉)、DI(Dependency Injection,依附注入)等編程准繩或許形式,而這些是有名 Java 框架 Spring、Struts 等的焦點地點。針對此查了 Wikipedia 中各個條目,並從藏書樓借來相干書本,浏覽後有些懂得,現聯合書中的講授和本身的加工整頓以下:
eg1
成績描寫:
開辟一個可以或許依照分歧請求生成Excel或 PDF 格局的報表的體系,例如日報表、月報表等等。
處理計劃:
依據“面向接口編程”的准繩,應當分別接口與完成,行將生成報表的功效提取為一個通用接口ReportGenerator,並供給生成 Excel 和 PDF格局報表的兩個完成類 ExcelGenerator 和 PDFGenerator,而客戶Client 再經由過程辦事供給者 ReportService 獲得響應的報表打印功效。
完成辦法:
依據下面所述,獲得以下類圖:
代碼完成:
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 { // 擔任創立詳細須要的報表生成器 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
成績描寫:
如下面代碼中的正文所示,詳細的報表生成器由 ReportService 類外部硬編碼創立,由此 ReportService 曾經直接依附於 PDFGenerator 或 ExcelGenerator ,必需清除這一顯著的緊耦合關系。
處理計劃:引入容器
引入一個中央治理者,也就是容器(Container),由其同一治理報表體系所觸及的對象(在這裡是組件,我們將其稱為 Bean),包含 ReportService 和各個 XXGenerator 。在這裡應用一個鍵-值對情勢的 HashMap 實例來保留這些 Bean。
完成辦法:
獲得類圖以下:
代碼完成:
class Container { // 以鍵-值對情勢保留各類所需組件 Bean private static Map<String, Object> beans; public Container() { beans = new HashMap<String, Object>(); // 創立、保留詳細的報表生起器 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 { // 清除緊耦合關系,由容器取而代之 // 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()); } }
時序圖年夜致以下:
後果:
如下面所示,ReportService 不再與詳細的 ReportGenerator 直接聯系關系,曾經用容器將接口和完成隔分開來了,進步了體系組件 Bean 的重用性,此時還可使用設置裝備擺設文件在 Container 中及時獲得詳細組件的界說。
eg3
成績描寫:
但是,不雅察下面的類圖,很輕易發明 ReportService 與 Container 之間存在雙向聯系關系,彼此相互有依附關系。而且,假如想要重用 ReportService,因為它也是直接依附於零丁一個 Container 的詳細查找邏輯。若其他容用具體分歧的組件查找機制(如 JNDI),此時重用 ReportService 意味著須要修正 Container 的外部查找邏輯。
處理計劃:引入 Service Locator
再次引入一個直接層 Service Locator,用於供給組件查找邏輯的接口,請看Wikipedia 中的描寫 或許 Java EE 對其的描寫1 、描寫2 。如許就可以夠將能夠變更的點隔分開來。
完成辦法:
類圖以下:
代碼完成:
// 現實運用中可所以用 interface 來供給同一接口 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 關於詳細組件的查找、創立的方法都是‘自動'的,這意味著作為客戶的 ReportService 必需清晰本身須要的是甚麼、到哪裡獲得、若何獲得。一會兒就由於 What、Where、How 而不能不增長了詳細邏輯細節。
例如,在後面‘引入Container '的完成辦法中,有以下代碼:
class ReportService { // 清除緊耦合關系,由容器取而代之 // private static ReportGenerator generator = new PDFGenerator(); // 經由過程 Container..getBean("reportGenerator") ‘自動'查找 private ReportGenerator generator = (ReportGenerator) Container .getBean("reportGenerator");
在‘引入 Service Locator '的完成辦法中,有以下代碼:
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 的外部常識(即查找組件的邏輯)。依據掌握反轉(IoC)准繩,可江此種拉(Pull,自動的)轉化成推(Push,主動的)的形式。
例如,日常平凡應用的 RSS 定閱就是Push的運用,省去了我們一天好幾回登錄本身愛好的站點自動獲得文章更新的費事。
而依附注入(DI)則是完成這類主動吸收、削減客戶(在這裡即ReportService)本身包括龐雜邏輯、知曉過量的弊端。
完成辦法:
由於我們願望是‘主動'的吸收,故照樣回到 Container 的例子,而不應用 Service Locator 形式。由此獲得修正後的類圖以下:
而本來的類圖以下,可以對比著看一下,留意正文的提醒:
代碼完成:
為了使例子可以或許編譯、運轉,而且略微應用跟蹤代碼的運轉成果來顯式全部類圖實例化、相互協作的前後次序,在各個類的結構器中參加了很多已編號的打印語句,和兩個可有可無的類,有點噜蘇,詳細以下:
import java.util.Date; import java.util.HashMap; import java.util.Map; // 為了可以或許編譯運轉,多了兩個可有可無的類 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 { // 以鍵-值對情勢保留各類所需組件 Bean privatestatic Map<String, Object> beans; public Container() { System.out.println("1...開端初始化 Container ..."); beans = new HashMap<String, Object>(); // 創立、保留詳細的報表生起器 ReportGenerator reportGenerator = new PDFGenerator(); beans.put("reportGenerator", reportGenerator); // 獲得、治理 ReportService 的援用 ReportService reportService = new ReportService(); // 注入下面已創立的詳細 ReportGenerator 實例 reportService.setReportGenerator(reportGenerator); beans.put("reportService", reportService); System.out.println("5...停止初始化 Container ..."); } publicstatic Object getBean(String id) { System.out.println("最初獲得辦事組件...getBean() --> " + id + " ..."); returnbeans.get(id); } } class ReportService { // private static ReportGenerator generator = new PDFGenerator(); // 清除下面的緊耦合關系,由容器取而代之 // private ReportGenerator generator = (ReportGenerator) Container // .getBean("reportGenerator"); // 去除下面的“自動”查找,供給公有字段來保留內部注入的對象 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()); } }
運轉成果:
1...開端初始化 Container ... 2...開端初始化 PDFGenerator ... 3...開端初始化 ReportService ... 4...開端注入 ReportGenerator ... 5...停止初始化 Container ... 最初獲得辦事組件...getBean() --> reportService ... generate an PDF report ...
留意:
1、依據下面運轉成果的打印次序,可見代碼中參加的詳細編號是公道的,模仿了法式履行的流程,因而也就不再畫序列圖了。
2、留意該例子中對IoC、DI的應用,是以ReportService為客戶端(即組件需求者)為基點的,而代碼中的Client 類main()中的測試代碼才是辦事組件的終究用戶,但它須要的不是組件,而是組件所具有的辦事。
3、現實在Spring框剪中,初始化Container明顯不是終究用戶Client應當做的工作,它應當由辦事供給方事前啟動停當。
4、在終究用戶Client中,我們照樣用到Container.getBean("reportService")來獲得事前已在Container的結構函數中實例化好的辦事組件。而在詳細運用中,平日是用XML等設置裝備擺設文件將可用的辦事組件安排到辦事器中,再由Container讀取該設置裝備擺設文件聯合反射技巧得以創立、注入詳細的辦事組件。
剖析:
之前是由ReportService自動從Container中要求獲得辦事組件,而如今是主動地期待Container注入(Inject,也就是Push)辦事組件。掌握權顯著地由底層模塊(ReportService 是組件需求者)轉移給高層模塊(Container 是組件供給者),也就是掌握反轉了。