Bản chất của Spring là tạo ra các đối tượng trong container của nó để khi các bạn cần thì có thể gọi đến đối tượng bạn cần. Vì thế, Spring tìm mọi cách để hỗ trợ chúng ta có thể tạo ra đối tượng trong khung chứa của nó một cách dễ dàng nhất.
Chúng ta đã biết cách khai báo để đưa đối tượng này vào đối tượng khác, hay có thể nói là đưa đối tượng phụ thuộc vào đối tượng bị phụ thuộc, bằng cách sử dụng thuộc tính ref, list … Ngoài ra, Spring còn hỗ trợ chúng ta đưa đối tượng phụ thuộc vào đối tượng bị phụ thuộc một cách tự động bằng cách sử dụng Bean Autowiring của nó.
Với Bean Autowiring, chúng ta ko cần phải sử dụng các thuộc tính ref hay list, mà chỉ cần viết code tuân thủ theo quy tắc của Spring thì đối tượng phụ thuộc sẽ tự động được đưa vào đối tượng bị phụ thuộc.
Có ba cách để sử dụng Bean Autowiring, đó là:
- byName
- byType
- constructor
Để các bạn có thể hiểu rõ hơn, mình sẽ làm một ví dụ nhé! Trong ví dụ này mình sẽ yêu cầu Spring tự động đưa đối tượng Table vào đối tượng Room của mình. Cụ thể như sau:
Mình sẽ sử dụng Java 17 cho ứng dụng ví dụ này:
1 2 3 4 |
<properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> </properties> |
Spring dependency như sau:
1 2 3 4 5 |
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>6.0.2</version> </dependency> |
Class Room:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
package com.huongdanjava.springbeanautowiring; public class Room { private Table table; public Room() {} public Room(Table table) { this.table = table; } public Table getTable() { return table; } public void setTable(Table table) { this.table = table; } } |
Class Table:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
package com.huongdanjava.springbeanautowiring; public class Table { private String code; public void setCode(String code) { this.code = code; } @Override public String toString() { return "Table [code=" + code + "]"; } } |
Class Application:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
package com.huongdanjava.springbeanautowiring; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Application { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml"); Room room = (Room) context.getBean("room"); if (room.getTable() != null) { System.out.println(room.getTable().toString()); } } } |
Và khai báo đối tượng Table và Room trong tập tin cấu hình của Spring:
1 2 3 4 5 6 7 8 9 10 11 12 |
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="table" class="com.huongdanjava.springbeanautowiring.Table"> <property name="code" value="123456" /> </bean> <bean id="room" class="com.huongdanjava.springbeanautowiring.Room"> </bean> </beans> |
OK, giờ mình sẽ đi lần lượt từng cách một và nói cho các bạn biết cụ thể về nó nhé!
byName
Như các bạn đã biết, mỗi bean trong Spring container đều có 1 id của riêng nó. Dựa vào điều này, Spring quy định nếu id của đối tượng phụ thuộc (trong ví dụ của mình là đối tượng Table) giống với tên một biến nào đó trong đối tượng bị phụ thuộc (đối tượng Room) và khi khai báo đối tượng bị phụ thuộc, chúng ta khai báo thêm thuộc tính autowire=”byName” thì Spring sẽ tự động đưa đối tượng phụ thuộc vào đối tượng bị phụ thuộc qua phương thức setter của đối tượng bị phụ thuộc.
Trong ví dụ của mình, id của đối tượng Table đã hoàn toàn giống với tên biến table trong đối tượng Room, do đó chúng ta chỉ cần khai báo thêm cho bean room thuộc tính autowire=”byName” thì Spring sẽ tự động đưa đối tượng Table vào đối tượng Room cho chúng ta.
Cấu hình của đối tượng Room sẽ sửa lại như sau:
1 2 |
<bean id="room" class="com.huongdanjava.springbeanautowiring.Room" autowire="byName"> </bean> |
Khi chạy, chương trình sẽ cho ra kết quả như sau:
Cách này có bất lợi ở chỗ nếu sau này chúng ta thay đổi tên biến của đối tượng bị phụ thuộc, lại phải đi đổi lại tên id của đối tượng phụ thuộc trong khung chứa của Spring và ngược lại. Các bạn hình dung được chứ nhỉ?
byType
Cách này chúng ta chỉ áp dụng được với những project nhỏ khi mà mỗi đối tượng chỉ có một bean duy nhất trong khung chứa của Spring. Khi đó bằng cách thay đổi thuộc tính autowire=”byType” khi khai báo một bean, Spring sẽ tự động đưa đối tượng phụ thuộc vào đối tượng bị phụ thuộc cũng qua phương thức setter của đối tượng bị phụ thuộc.
1 2 |
<bean id="room" class="com.huongdanjava.springbeanautowiring.Room" autowire="byType"> </bean> |
Kết quả:
Nếu các bạn cố tình khai báo thêm một bean khác của đối tượng phụ thuộc như sau:
1 2 3 4 5 6 7 8 9 10 |
<bean id="table" class="com.huongdanjava.springbeanautowiring.Table"> <property name="code" value="123456" /> </bean> <bean id="table1" class="com.huongdanjava.springbeanautowiring.Table"> <property name="code" value="123456" /> </bean> <bean id="room" class="com.huongdanjava.springbeanautowiring.Room" autowire="byType"> </bean> |
Thì khi chạy sẽ gặp lỗi:
constructor
Giống như thuộc tính autowire=”byType”, cách này chúng ta cũng chỉ áp dụng khi đối tượng của chúng ta chỉ có một bean duy nhất trong khung chứa của Spring. Khi đó, Spring sẽ tự động đưa đối tượng phụ thuộc vào đối tượng bị phụ thuộc qua constructor của đối tượng bị phụ thuộc.
Ví dụ, chúng ta thay đổi cấu hình của bean room như sau:
1 2 |
<bean id="room" class="com.huongdanjava.springbeanautowiring.Room" autowire="constructor"> </bean> |
Kết quả:
Tuấn Bảo
Cảm ơn bài viết của anh.
ilovebka56
Hi anh, sau khi test thì em thấy by contructor giống với by Name hơn, bởi Khi em khai báo các bean cùng type trong spring.xml thì khi chạy không có bất kì một exception nào được ném ra, ngược lại thì chương trình vẫn chạy bình thường và chỉ bean có id trùng với tên của tham số trong contructor thì mới được inject. A thử check lại trường hợp này xem nhé. Thanks anh nhiều. 🙂
Khanh Nguyen
Vấn đề là chúng ta cần bean có thể inject được vào đối tượng chúng ta cần, đúng không em?
2115
A cho e hỏi : trong vd autowire byName e thử sửa tên bên table trong class Rôm thành private Table tbl; trong spring.xml làm y hệt vd trên. V tại sao e vẫn lấy được giá trị 123456 ghi id Table trong spring là table, còn tên biến trong Room là tbl ?
Khanh Nguyen
Em share code liên quan đến cái mà em đang hỏi cho anh đi? Anh xem thử nguyên nhân là gì!
Kim Cuong
em cảm ơn anh nhiều! Cho em hỏi là có đối tượng nào không autowire không? Và nếu có thì là những đối tượng nào?
Khanh Nguyen
Đối tượng nào mình cũng có thể autowire hết em. Tuỳ bài toán thì mình có thể có cách những giải quyết khác nhau.
Hoang long
giả sữ có 2 bean cùng loại thì autowire như thế nào hả anh (trong vd autowire by type)
Khanh Nguyễn
Trong trường hợp này, em có thể chuyển qua sử dụng annotation @Autowired để autowire, sau đó sử dụng annotation @Qualifier để chỉ định bean mà em cần autowire.
Xem chi tiết ở đây nhé em http://huongdanjava.com/su-dung-annotation-qualifier-trong-spring.html
Vinh
Cảm ơn anh vì bài viết này!
giacnui
hình như class Room thiêú constructor ko tham số.
Khanh Nguyễn
Cảm ơn em đã phát hiện ra chỗ đó.
Do lúc nói đến cái autowire bằng constructor anh có thêm constructor để giải thích chỗ đó mà không để ý nó sẽ bị lỗi hai cái đã nói vì ko có default constructor.
Thanks em một lần nữa.
Đã sửa lại.
giacnui
good, em đợi bài viết của anh mấy ngày rồi. hy vọng anh làm seri từ đầu đến cuối luôn, có nhiều chỗ hướng dẫn nhưng nửa vời quá, học ko hiệu quả.
Khanh Nguyễn
Chỗ nào anh hướng dẫn chưa kỹ, em cứ nói để anh sửa lại nhé! Anh mong nhận được đóng góp ý kiến từ tất cả mọi người.