Trong bài viết trước, mình đã giới thiệu với các bạn cơ bản về Constructor Injection trong Spring sử dụng tập tin XML. Trong bài viết này, mình xin đề cập về nó một cách chi tiết hơn để các bạn có thể sử dụng nó một cách dễ dàng các bạn nhé!
Hãy xem xét ví dụ sau, mình có class Student:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
package com.huongdanjava.springconstructorinjection; public class Student { private String name; public Student(String name) { this.name = name; } @Override public String toString() { return "Student [name=" + name + "]"; } } |
và class Clazz:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
package com.huongdanjava.springconstructorinjection; public class Clazz { private String name; private Student student; public Clazz(String name, Student student) { this.name = name; this.student = student; } @Override public String toString() { return "Clazz [name=" + name + ", student=" + student + "]"; } } |
Như các bạn thấy, class Clazz của mình đã định nghĩa một constructor với tham số là một đối tượng Student và một biến name kiểu String. Các tham số này là những đối tượng phụ thuộc, vậy làm thế nào để inject các đối tượng phụ thuộc này vào class Clazz sử dụng constructor này? Các bạn đọc tiếp nhé!
Trước tiên, để dễ hiểu, mình sẽ tạo mới một Maven project trước:
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 Application có nội dung như sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
package com.huongdanjava.springconstructorinjection; import org.springframework.beans.factory.BeanFactory; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Application { public static void main(String[] args) { BeanFactory context = new ClassPathXmlApplicationContext("spring.xml"); Clazz clazz = (Clazz) context.getBean("clazz"); System.out.println(clazz.toString()); } } |
OK, bắt đầu nào các bạn.
Để inject đối tượng Student vào đối tượng Clazz, trước tiên chúng ta cần khởi tạo đối tượng Student trong Spring container trước! Ví dụ giờ mình khởi tạo đối tượng Student với tên là “Khanh”, mình sẽ khai báo trong khung chứa của Spring như sau:
1 2 3 |
<bean id="student" class="com.huongdanjava.springconstructorinjection.Student"> <constructor-arg value="Khanh" /> </bean> |
Với cách khai báo này, Spring sẽ gọi đến constructor
1 2 3 |
public Student(String name) { this.name = name; } |
để khởi tạo đối tượng Student.
Trong khai báo này, mình đã sử dụng thẻ <constructor-arg> với thuộc tính value để gán giá trị cho biến name. Thẻ <constructor-arg> này được sử dụng để định nghĩa các tham số của constructor cho các đối tượng trong khung chứa của Spring. Nó có nhiều thuộc tính nhưng có 2 thuộc tính mà chúng ta thường dùng với thẻ này là value và ref.
Thuộc tính value được sử dụng trong trường hợp giá trị inject là một giá trị text, số hoặc là boolean,… Còn thuộc tính ref thì được sử dụng để reference tới một bean khác đã được khởi tạo trong Spring container.
OK, vậy là chúng ta đã khai báo xong đối tượng Student, bây giờ đến đối tượng Clazz các bạn nhé!
Để khai báo đối tượng Clazz, chúng ta sẽ sử dụng thẻ <constructor-arg> cùng với cả 2 thuộc tính ref và value luôn theo thứ tự từng tham số, như sau:
1 2 3 4 |
<bean id="clazz" class="com.huongdanjava.springconstructorinjection.Clazz"> <constructor-arg value="A" /> <constructor-arg ref="student" /> </bean> |
Kết quả:
Nếu các bạn muốn khai báo các tham số theo những thứ tự khác nhau, chúng ta có thể khai báo như sau:
1 2 3 4 |
<bean id="clazz" class="com.huongdanjava.springconstructorinjection.Clazz"> <constructor-arg index="1" ref="student" /> <constructor-arg index="0" value="A" /> </bean> |
Giá trị của index bắt đầu từ 0 nhé các bạn.
Ngoài ra, thay vì sử dụng thẻ <constructor-arg>, Spring còn hỗ trợ cho chúng ta một cách khác đơn giản hơn để làm việc với Constructor Injection, đó chính là sử dụng namespace c, xmlns:c=”http://www.springframework.org/schema/c”.
Namespace này cho phép chúng ta có thể khai báo các tham số trong constructor của các đối tượng ngay trong thẻ <bean> dưới dạng là một thuộc tính. Nó không đi kèm một tập tin schema XSD, do đó để sử dụng namespace c, các bạn chỉ cần khai báo nó trong tập tin cấu hình XML của Spring như sau:
1 2 3 4 5 6 |
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:c="http://www.springframework.org/schema/c" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> </beans> |
rồi sử dụng syntax sau để inject các đối tượng phụ thuộc.
Nếu các bạn sử dụng tên của các tham số trong constructor để inject các đối tượng phụ thuộc thì hãy khai báo một thuộc tính trong thẻ <bean> với name bắt đầu là “c:” tiếp theo là tên của tham số và kết thúc là “-ref” nếu đối tượng phụ thuộc là một bean khác trong khung chứa của Spring, sau đó gán cho nó một giá trị. Ví dụ mình có thể khai báo đối tượng Student như sau:
1 2 |
<bean id="student" class="com.huongdanjava.springconstructorinjection.Student" c:name="Khanh"> </bean> |
Còn nếu các bạn sử dụng index để khai báo các tham số thì thì hãy khai báo một thuộc tính trong thẻ <bean> với name bắt đầu là “c:_” tiếp theo là index và cuối cùng là “-ref” nếu đối tượng phụ thuộc là một bean khác trong khung chứa của Spring, sau đó gán giá trị cho nó. Ví dụ mình có thể khai báo đối tượng Clazz như sau:
1 2 |
<bean id="clazz" class="com.huongdanjava.springconstructorinjection.Clazz" c:_0="A" c:_1-ref="student"> </bean> |
Kết quả khi chạy lại ví dụ vẫn như trên:
Ngọc
em muốn hỏi là nếu class có nhiều hơn 1 constructor vd :
pubic class Student{
private String name;
private String className;
public Student(String name){
this.name = name;
}
public Student(String className){
this.className = className;
}
}
thì trong thẻ constructor-arg thuộc tính value sẽ set giá trị có name, hay className trước ạ ? E cảm ơn
Khanh Nguyen
Có gì đó sai sai với 2 constructors như thế này em nhé.
long
https://huongdanjava.com/vi/inject-doi-tuong-nay-vao-doi-tuong-khac-trong-spring-su-dung-tap-tin-xml.html
trong bai nay, anh cung tao 2 constructor co 1 doi so.
Khanh Nguyen
Nhưng mà trong ví dụ của anh, kiểu dữ liệu khác nhau nha em! Của em thì 2 constructor cùng kiểu dữ liệu là String, sai là ở chỗ này đó!
Giang
anh ơi cho em hỏi tại sao trong cái này cái này : System.out.println(clazz.toString()); nó lại gọi lại được hàm student.toString() luôn hả anh
Giang
public String toString() {
return “Clazz [name=” + name + “, student=” + student + “]”;
}
trong cái này student là đối tượng nên em không hiểu tại sao nó lại trả ra cái phương thức student.toString() luôn :/
Khanh Nguyen
Đúng rồi đó em!
Mặc định nếu em in một đối tượng thì Java sẽ gọi hàm toString() của đối tượng đó để thực thi mà.
Quang
Cho mình hỏi BeanFactory với ApplicationContext trong các bài trước có gì khác nhau không?
Khanh Nguyen
Bạn nên xem bài viết này nhé! http://huongdanjava.com/beanfactory-va-applicationcontext-trong-sping.html