Autowired trong spring java?

em có 1 số câu hỏi về spring mvc trong java

  1. khi em tạo spring repository , em làm theo hướng dẫn trên mạng thì sẽ viết thế này
@Repository
public interface BookRepository extends CrudRepository<Bookmaster, Integer> {
   // code crud
}

use : 
@autowired
BookRepository bookRepository ;

vậy cho em hỏi cái đối tượng bookRepository nó sẽ được khởi tạo từ lớp nào , vì em thấy bản thân nó là interface thì chắc chắn sẽ phải có 1 class implement nó thì mới tạo đối tượng được nhưng trong spring chỉ cần dùng autowired là nó tự động tạo đối tượng cho mình?

  1. theo em được biết các bean mà ta khai báo để container thực hiện việc inject vào các class sẽ được xậy dựng theo singleton >>> tức là mỗi bean sẽ là duy nhất trong toàn bộ ứng dụng
    nhưng đối với 1 webapp bằng java thì sẽ có nhiều request gởi đến cùng lúc và muốn sử dụng các bean này >>> vậy ở đây xảy ra điều gì ? 1. cùng 1 bean phục vụ cho nhiều thread và container sẽ đồng bộ hóa dữ liệu giữ các thread >>> nhưng thế này em thấy không hợp lý vì lỡ như có cả triệu request mà dùng chung 1 bean thì sẽ dễ bị nút cổ chai , 2. nó tạo ra nhiều bean mỗi lần có request gửi tới nó sẽ inject bean đó vào thread
1 Like
  1. Không phải các bean được xây dựng theo singleton đâu bạn, xây dựng theo singleton hay Factory là phụ thuộc vào cách ta viết thôi.
  1. bookRepository sẽ chỉ đc khởi tạo nếu class reference tới nó là 1 bean. Còn thằng chịu trách nhiệm khởi tạo bookRepository là Spring IoC container. Lúc app của bạn boot lên, thằng container này sẽ đi quét toàn bộ các beans hiện có trong cây thư mục, sau đó nó sẽ thấy 1 bean đang cần sử dụng instance của BookRepository, nó sẽ tiến hành inject vào biến bookRepository của bean đó để bạn có thể sử dụng.
  1. Không phải lúc nào bean được tao ra cũng theo dạng singleton. Mặc định khi tạo bean nếu bạn ko chỉ định gì thêm thì nó mới tạo bean dưới dạng singleton. Khi 1 bean được tạo ra dưới dạng singleton, Spring IoC container sẽ tạo 1 và chỉ 1 instance của bean đó và lưu nó dưới cache. Với singleton, người ta không sửa đổi dữ liệu của bean vì nó là shared instance và dữ liệu có thể bị sai lệch giữa các thread đang dùng bean đó. Cuối cùng, một bean hoàn toàn có thể được access bởi hàng triệu request, ko vấn đề gì nếu dữ liệu của nó nhất quán giữa các thread và không có sự can thiệp chỉnh sửa dữ liệu của bean.

Tại sao nó làm được vậy? Ví dụ như bean bookRepository ở trên, vùng nhớ của bean này nằm ở heap của JVM. Giả sử có nhiều request xảy ra cùng 1 lúc (concurrent requests) và đều gọi tới cùng 1 bean này, chương trình sẽ trỏ tới vùng nhớ đó và gọi ra đoạn code bên trong bean mà mỗi request yêu cầu. Mỗi request chạy trong một thread riêng, đoạn code cũng được thực thi trong mỗi thread riêng ko ảnh hưởng gì nhau => không bao giờ có chuyện chờ nhau mà xảy ra nút cổ chai được.

5 Likes

Câu hỏi của bạn rất hay.

  1. Để trả lời được câu hỏi này, mình đã bật logging để xem cách spring khởi tạo các bean theo trình tự như thế nào, rồi debug vào tận bên trong của method của Repository.
    (Ở đây mình dùng project đang có sẵn, kế thừa JpaReposoty. Tuy nhiên nguyên lý thì cũng tương tự như bạn kế thừa CrudRepository thôi)

Kết quả mình tìm hiểu được có thể tóm tắt như sau:

  • Các bean được khai baó bằng @Repository sẽ được khởi tạo bởi JpaRepositoryFactory
    Có nghĩa là mặc dù chỉ khai báo interface, nhưng spring đã tự động tạo ra object tương ứng của interface bằng cách sử dụng class JpaRepositoryFactory.

  • Khi 1 method khai báo trong interface @Repository được gọi thì spring sẽ gọi đến JpaQueryExecution để xử lý.
    – Tại sao spring lại gọi được class JpaQueryExecution?
    Lý do là vì trong lifecycle của SpringBean, sau khi 1 bean được tạo xong, spring sẽ gọi 1 class dạng BeanPostProcessor. Và spring-data-jpa đã tận dụng class này để gắn JpaQueryExecution vào @Repository.
    Tên của class BeanPostProcessor này là CrudMethodMetadataPostProcessor
    – Tại sao có những method chỉ có khai báo trong interface thôi (VD: trong hình là method findByEmail), mà spring vẫn biết cách để tạo thành câu query để thực hiện?
    Lý do là spring đã tách các thành phần của method (vd: từ khóa Email) và phân tích thành field, rồi biến thành điều kiện của câu query. Xem PartTreeJpaQuery

  • Cuối cùng là gọi đến EntityManager.createQuery để tạo ra câu query tương ứng với loại DB mà ứng dụng đang dùng, sau đó là execute. Bước này là thuộc phần công việc của Java JPA, nên mình ko tìm hiểu thêm nữa.

Bạn có thể xem thêm ở stacktrace khi debug

  1. Về cơ bản thì các spring bean sẽ là singleton (mặc định của spring bean là singleton). Tuy nhiên bạn cũng có thể khai báo scope khác.
    – Mặc dù chỉ có 1 object (singleton), nhưng thực tế các bean này chỉ chứa các method. Mỗi request gửi đến thì 1 method được gọi để thực hiện xử lý và trả về kết quả. Kiểu như là mỗi lần gọi sẽ thực hiện tính toán ở 1 vùng nhớ khác nhau, 10 requests đến thì tính toán ở 10 vùng nhớ khác nhau và trả về 10 kết quả khác nhau. Và 10 xử lý này độc lập, ko ảnh hưởng lẫn nhau.
    – Còn khi có 1 triệu request gửi đến thì sao :slight_smile: ?
    Thường mỗi máy chủ chỉ xử lý được 1 lượng request nhất định (vd: 100,000 requests).
    Khi ứng dụng có quá nhiều request gửi đến thì phải cân nhắc chỉnh sửa kiến trúc hệ thống. VD: sử dụng load balancer để chia tải
    Chắc bạn cũng nghe nói Google, Facebook có rất nhiều máy chủ đặt ở khắp nơi trên thế giới đúng không?
    Cho nên vấn đề ko phải là nút cổ chai ở singleton, mà là bạn cần sử dụng nó trong những tình huống phù hợp.

Happy coding.

5 Likes
83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?