Dependency injection nghe có vẻ phức tạp nhưng thực thế nó không phức tạp như những gì ta nghe thấy. bởi việc ứng dụng DI vào dự án của bạn . bạn sẽ thấy rằng code của bạn sẽ trở nên đơn giản , dễ hiểu và đơn giản để test.
DI làm việc thế nào
Mọi ứng dụng nho nhỏ (ít nhất cũng phải to hơn cái hello would) thì đều có từ hai tới nhiều classs, chúng hợp tác với nhau để thực hiện một số logic nghiệp vụ. Theo truyền thống, mỗi object thì chịu trách nhiệm cho việc thu thập những quan hệ với những đối tượng mà nó hợp tác (dependency) . Nó có thể dẫn tới khó để test code
Ví dụ , consider class Knignt để xem :
Bạn có thể thấy DamseRescuingKnight (chiến binh giải cứu thiếu nữ) Tạo chiến dịch của nó, một chiến dịch giải cứu (RescueDamselQuest) trong hàm khởi tạo. Điều này sẽ tạo ra một DamselRescuingKnight gắn kết chặt chẽ với RescueDamselQuest và giới hạn nghiêm trọng những chiến dịch mà người chiến binh có thể tham ra. Nếu thiếu nữ cần giải cứu thì cần chiến binh này. Nhưng nếu cần giết hoặc đơn giản là quay một cái bàn tròn thì người chiến binh này sẽ không thể làm gì hết vì họ chỉ biết giải cứu mà thôi.
Hơn nữa, sẽ khó khủng khiếp khi viết một unit test cho DamselRescuingKnight. Trong một test, bạn muốn chắc chắn rằng hàm embark của chiến dịch sẽ được chạy khi hàm embarkOnQuest() được gọi. Nhưng không có một cách rõ để hoàn thành nó. Thật không may, DamselRescuingKnight sẽ vẫn không được kiểm tra
Sự kết hợp có hai mặt. Một mặt, Code kết hợp chặt chẽ thì khó để test, khó để tái sử dụng và khó để hiểu và nó thưởng xảy ra trường hợp “whack-amole” bug( nghĩa là fix được một bug thì lại làm ra mấy bug nữa ). Trên mặt khác, Một lượng nhất định sự kết hợp lại cần thiết- hoàn toàn không gắn kết code sẽ không làm bất kì điều gì. Để thực hiện nhiều điều . những class cần để biết về nhau theo cách nào đó. Liên kết thì cần thiết nhưng nên được quản lý
Với DI thì đối tượng sẽ đưa những sự phụ thuộc tại thời điểm khởi tạo cho một bên thứ ba cái mà điều phối từng đối tượng trong hệt thống. Object thì không được dự định để tạo ra hoặc đạt được những ràng buộc của chúng.
Hình 1.1 những ràng buộc thì được tiêm vào các object

Để làm rõ điểm này, hãy xem BraveKnight giưới đấy . một chiến binh không chỉ giải cứu mà có thể tham ra mọi chiến dịch khác nữa:

Bạn có thể thấy BraveKnight không dống như DamselRescuingKnight, không tự tạo ra chiến dịch cho anh ấy , thay vào đó anh ấy đưa việc tạo ra chiến dịch tại thời điểm khởi tạo như một tham số khởi tạo. Cái này là kiểu mà DI gọi là constructor injection
Hơn thế nữa, chiến dịch cái mà ánh ấy đã được đưa là kiểu Quest, một interface cái mà toàn bộ chiến dịch(quests) đều implement. Nên BraveKnignt có thế tham ra tất cả các loại chiến dịch bao gồm RescueDamselQuest(chiến dịch giải cứu), a SlayDragonQuest(chiến dịch diệt rồng), a MakeRoundTableRounderQuest , hoặc mọi chiến dịch khác được implement từ Quest
Điểm này là BraveKingt thì không liên kết với bất kì một class đặc biệt nào implement từ Quest , Nó không quản lý loại hình chiến dịch mà anh ấy tham ra, chỉ cần loại hình chiến dịch đó được implement từ inteface Quest. Cái này là một đặc thù của DI – loose coupling. Nếu một object chỉ biết về những ràng buộc của nó thông qua các interface (chứ không phải các lớp cụ thể implement từ interface đó), sau đó những ràng buộc có thể được thay đổi qua lại giữa các class implement từ interface
Bây giờ , class BraveKnight đã được viết như cách mà bạn có thể đưa cho chiến binh(knight) đó mọi chiến dịch bạn muốn. Làm thế nào để xác định một Quest để đưa cho anh ấy . giả sử bạn muốn cho BraveKnight tham ra một chiến dịch diệt rồng. SlayDragonQuest được thể hiện như sau :

Như bạn thấy, SlayDragonQuest được implement từ interface Quest, điều làm thành môt điều phù hợp với BraveKinght, bạn có thể nhận thấy rằng thay vì sử dụng System.out .println() như nhiều ví dụ java đơn giản thì SlayDragonQuest đã yếu cầu một PrintStream qua hàm khởi tạo của nó. Câu hỏi đặt ra ở đấy là làm thế nào để đưa SlayDragon cho BraveKnight và làm thế nào để đưa PrintStream cho SlayDrasonQuest?
Cái hành động để tạo các mối liên hệ giữa các thành phần của ứng dụng thì thường được gọi là wiring. Trong spring , có nhiều cách để wire các thành phần cùng nhau, nhưng một trong những các phổ biến là sử dụng XML.Hãy xem một ví dụ đơn giản về file cấu hình Knight.xml, cái wires một BraveKnight một SlayDragonQuest và một PrintStream cùng nhau.

Ở đây, BraveKnight và SlayDragonQuest thì được khai báo như các Beans trong spring. Trong hoàn cảnh đó của BraveKnight bean. Nó thì được khởi tạo và truyền một tham chiếu tới SlayDragonQUest bean như một tham số khởi tạo.
Nếu cấu hỉnh XML không phải cách bạn muốn thì bán có thể cấu hình nó sử dụng java. Ví dụ :

Bất kể bạn sử dụng XML hay Java để cấu hình . thì lợi ích của DI vẫn như nhau thôi . Mặc dù Brave phục thuộc vào một Quest, nó không biết về cái kiểu của Quest mà nó sẽ được đưa hoặc Quest đó đến từ nới nào. Tương tự thế, SlayDragonQuest phụ thuộc vào PrintStream nhưng nó không được biết rằng làm thế nào PrintStream đến được. Chỉ duy nhất Spring, thông qua cấu hình của nó biết được làm thế nào để toàn bộ các thành phần đến được với nhau. Nó khiến chúng ta có khả năng thay đổi các sự phụ thuộc giữa các thành phần mà không làm thay đổi đến các class phụ thuộc:
Trong ví dụ này chúng ta sẽ xem một hướng thiếu cận đơn giản để wiring beans trong spring. Không quan tâm quá nhiều tới các chi tiết bấy giờ. Chúc ta sẽ làm nhiều hơn với cấu hình spring trong chaper 2. Chúng ta cũng sẽ thấy những cách khác nhau để wrired trong spring bao hồm một cách đó là để spring tự động phát hiện các bean và tạo quan hệ giữa chúng
Bây giờ, bạn đã khai báo mối quan hệ giữa BraveKnight và Quest. Bạn cần load cấu hình XML và bắt đầu ứng dụng
Cài đặt cho ứng dụng chạy
Trong ứng dụng spring , một application context sẽ tài những bean được định nghĩa và wires chúng với nhau. Spring application context chịu hoàn toàn trách nhiệu cho việc tạo và wiring của các đối tượng tạo nên ứng dụng.
Khi các bean trong knight.xml được khai báo trong XML file . Một lựa chọn thích hợp cho application context có thể là ClassPathXMLApplicationContext. Spring Context này sẽ thực hiện tải spring context từ một hoặc nhiều file XML được đặt trong classpath của ứng dụng. Ví dụ với knight.xml:
Đây là phương thức main() tạo Spring application context dựa trên Knights.xml . sau đó nó sử dụng application context như một nhà máy để lấy lại bean có ID là knight. Với một liên hệ tới object Knight. Nó gọi tới phương thức embarkOnQUest() để các hiệp sĩ tham ra các chiến dịch mà anh ấy nhận được chú ý rằng class không biết về điều gì về kiểu chiến dịch Ques mà người anh hùng đó nhận được (ý rằng lớp Knight không biết về loại lớp con nào của Quest được truyền vào mà chỉ biết tới interface Quest). Chỉ duy nhất knight.xml biết chính sác một lớp implementation (ý là lớp implement lại interface Quest) nào được truyền vào.