Thấy có nhiều người mới tìm hiểu về Spring Framework mà không biết bắt đầu từ đâu hay học như thế nào nên mình xin viết 1 tutorial về Spring Framework theo 1 cách bình dân dễ hiểu nhất. Bài viết dành cho những bạn đã có kiến thức cơ bản về JAVA và hiểu được 1 số concept lập trình java/web cơ bản.
Nếu bạn thực sự muốn tìm hiểu sâu, và bạn có thể đọc tài liệu tiếng anh,thì mình khuyên các bạn nên lên trang chủ của Spring để tìm hiểu thay vì đọc bài này :
https://docs.spring.io/spring/docs/current/spring-framework-reference/index.html
Còn nếu khả năng đọc tiếng anh của bạn chưa được tốt và bạn muốn có 1 cái nhìn tổng quát về Spring thì welcome bạn tới khóa bình dân học vụ của mình.
Bài viết cũng chỉ dựa trên kiến thức cá nhân thu nhặt được sau mấy năm code nên nếu có gì sai thì nhờ các senior vào chỉnh sửa. Vì cũng không có quá nhiều thời gian nên 2 ~ 3 ngày mình sẽ viết 1 bài.
Nội dung để các bạn tiện theo dõi :
- Mở đầu : Spring là gì, tại sao chúng ta lại cần Spring.
- Spring core : Bean & Bean container.
- Spring MVC
- Spring DATA - Spring JPA
Ở đây mình chỉ trình bày 4 nội dung cơ bản, phổ biến như trên vì ngoài ra Spring còn rất nhiều component khác mà không thể trình bày hết trong 1 vài bài viết được. Trong đó có 1 phần khá quan trọng là Spring AOP, các bạn lưu ý tìm hiểu thêm. Rồi, không dài dòng nữa chúng ta vào phần chính của bài viết.
1. Mở đầu : Spring là gì, tại sao chúng ta lại cần Spring.
Well, Nói đơn giản Spring là 1 framework cho phép chúng ta có thể tách các thành phần trong một ứng dụng độc với nhau, giảm sự phụ thuộc lẫn nhau, khi thay đổi 1 thành phần thì các thành phần khác sẽ không bị ảnh hưởng nhiều. Hay nói cách khác, nó giúp cho ứng dụng của chúng ta dễ dàng mở rộng và bảo trì, giảm thiếu cost.
Vậy sự phụ thuôc ở đây nghĩa là gì, cụ thể như thế nào? Xét ví dụ sau :
Giả sử bạn đang implement 1 chức năng upload file lên server. Boss của bạn quyết định sử dụng google drive làm nơi lưu trữ. Khi đó chương trình sẽ được viết đại khái như sau
class FileController {
public void uploadFile(File file) {
GoogleDriveService gds = new GoogleDriveService();
// setup google authentication, targer folder, etc..
gds.upLoad(file);
}
}
Sau đó bạn tiến hành viết unit test cho FileController và GoogleDriveService, chức năng của bạn chạy rất ổn với đoạn code trên. Nhưng đến 1 ngày đẹp trời, boss của bạn dở chứng, yêu cầu bạn thay vì lưu trên Google Drive, thì chuyển qua S3 của Amazon. (là 1 service lưu trữ file trực tuyến khác). Khi đó, bạn phải update lại toàn bộ code FileController, sửa lại GoogleDriveService thành S3Service chẳng hạn.
Điều oái ăm ở đây là, không phải mỗi class FileController sử dụng GoogleDriveService, mà có rất nhiều controller khác sử dụng nữa (AccountController, ProductController…ect). Vậy là bạn phải mở banh toàn bộ project và sửa lại hết và viết lại toàn bộ unit test cho controller (vì source controller đã bị thay đổi nên bắt buộc phải test lại) trong nước mắt, trong đầu nghĩ mai tao sẽ nghỉ việc!
Như vậy điều rút ra được ở đây là, class FileController đã quá phụ thuộc vào class GoogleDriveService, khiến cho việc maintain, extend trở nên khó khăn hơn. Vì vậy người ta đưa ra những giải pháp khác nhau để khắc phục vấn đề này, trong đó nổi bật nhất là FactoryPattern hoặc Ioc. (đảo ngược sự phụ thuộc)
Chỉ tiết thì các bạn xem qua link ở trên, ở đây mình chỉ giải thích 1 cách cơ bản nhất về IoC.
Quay lại ví dụ ở trên, để khắc phục vấn đề ở trên, Có thể code lại chương trình như sau :
interface FileService {
void uploadFile(File file);
}
class GoogleDriveService implements FileService {
public void uploadFile(File file) {
// setup google authentication, targer folder, etc..
// do upload
}
}
class FileController {
private FileService fileService;
public FileController(FileService fileService) {
this.fileService = fileService;
}
public void uploadFile(File file) {
this.fileService.uploadFile(file);
}
}
class ApplicationContext {
public FileService getFileService() {
return new GoogleDriveService();
}
public Controller getFileController() {
return new FileController(this.getFileService());
}
}
Trong đoạn code trên, chúng ta tạo 1 interface FileService, và class GoogleFileService implements nó. Class FileController chứa 1 biến có kiểu file FileService và việc khởi tạo controller chúng ta ủy thác lại cho class ApplicationContext, trong đó nó sẽ tự khởi tạo GoogleFileService, truyền giá trị này cho FileController qua constructor, và biến fileService sẽ nhận giá trị là 1 instance của GoogleFileService (Tính đa hình thể hiện ở đây).
Và kết quả là, khi đó class FileController thoát khỏi sự phụ thuộc của GoogleDriveService, thực tế là FileController nó không cần phải biết FileService là cái gì, là instance cụ thể nào, việc của nó chỉ là gọi 1 lời gọi hàm để upload fle.
Kỹ thuật coding này gọi là IoC - đảo ngược sự phụ thuộc, và được hiện thực hóa bằng 1 khái niệm gọi là Dependency Injection, giá trị phụ thuộc được inject - truyền (tiêm - dịch nghe chuối vãi) vào class thông qua Constructor ( Ngoài ra có thể qua setter nếu bạn muộn ).
Khi này, nếu xếp của bạn yêu cầu đổi sang Amazon S3, thì việc của bạn chỉ là create 1 class S3Service, implements interface FileService ( viết code để upload lên S3), sau đó thì sửa lại application context :
class ApplicationContext {
public FileService getFileService() {
return new S3Service(); // change sang S3Service
}
public Controller getFileController() {
return new FileController(this.getFileService());
}
}
Và bây giờ bạn chỉ phải viết test case cho mỗi class S3Service, source của Controller không hề thay đôi, bạn hoàn toàn có thể yên tâm về nó được rồi
Trong thực tế,(như Spring chẳng hạn) , ở method getFileService của ApplicationContext, người ta sẽ không khởi tạo trực tiếp object như thế, mà sẽ dựa vào config ( bằng XML file hoặc Annotation, sẽ viết rõ hơn ở bài sau) để quyết định instance nào, thông qua 1 kỹ thuật reflection hoặc dựa vào 1 business logic nào đấy để quyết định trả về instance nào.
Dựa trên nguyên lý chia tách này, Spring framework ra đời! Và việc khởi tạo quản lý vòng đời, cũng như các object cần quản lý như trên sẽ đưa chúng ta tới khái niệm ApplicationContext, Bean trong Spring. Mời các bạn đón đọc tiếp về phần 2 ở bài viết sau.