Hiểu về cách sử dụng Spring @Autowired


309

Tôi đang đọc tài liệu tham khảo mùa xuân 3.0.x để hiểu chú thích Spring Autowired:

3.9.2 @Autowired và @Inject

Tôi không thể hiểu các ví dụ dưới đây. Chúng ta có cần phải làm một cái gì đó trong XML để nó hoạt động không?

VÍ DỤ 1

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Autowired
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // ...
}

VÍ DỤ 2

public class MovieRecommender {

    private MovieCatalog movieCatalog;

    private CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    public void prepare(MovieCatalog movieCatalog,
                    CustomerPreferenceDao customerPreferenceDao) {
        this.movieCatalog = movieCatalog;
        this.customerPreferenceDao = customerPreferenceDao;
    }

    // ...
}

Làm thế nào hai lớp có thể được tự động thực hiện cùng một giao diện và sử dụng cùng một lớp?

Thí dụ:

class Red implements Color
class Blue implements Color

class myMainClass{
    @Autowired 
    private Color color;

    draw(){
        color.design(); 
    } 
}

Phương pháp thiết kế nào sẽ được gọi là? Làm thế nào để tôi chắc chắn rằng phương thức thiết kế của lớp Red sẽ được gọi chứ không phải Blue?

Câu trả lời:


542

TL; DR

Chú thích @Autowired cho phép bạn cần tự mình thực hiện nối dây trong tệp XML (hoặc bất kỳ cách nào khác) và chỉ tìm cho bạn những gì cần được tiêm ở đâu và thực hiện điều đó cho bạn.

Giải thích đầy đủ

Các @Autowiredchú thích cho phép bạn bỏ qua các cấu hình khác về những gì để tiêm và chỉ làm điều đó cho bạn. Giả sử gói của bạn là com.mycompany.moviesbạn phải đặt thẻ này vào XML (tệp ngữ cảnh ứng dụng):

<context:component-scan base-package="com.mycompany.movies" />

Thẻ này sẽ tự động quét. Giả sử mỗi lớp phải trở thành một bean được chú thích với một chú thích chính xác như @Component(đối với bean đơn giản) hoặc @Controller(đối với điều khiển servlet) hoặc @Repository(đối với DAOcác lớp) và các lớp này nằm ở đâu đó trong gói com.mycompany.movies, Spring sẽ tìm tất cả các lớp này và tạo một hạt cho mỗi người. Điều này được thực hiện trong 2 lần quét các lớp - lần đầu tiên nó chỉ tìm kiếm các lớp cần trở thành một hạt và ánh xạ các mũi tiêm cần thực hiện, và trong lần quét thứ hai, nó tiêm hạt đậu. Tất nhiên, bạn có thể định nghĩa các bean của mình trong tệp XML truyền thống hơn hoặc với lớp @Configuration (hoặc bất kỳ kết hợp nào của ba).

Các @Autowiredchú thích cho mùa xuân nơi tiêm cần phải xảy ra. Nếu bạn đặt nó vào một phương thức, setMovieFindernó sẽ hiểu (bằng tiền tố set+ @Autowiredchú thích) rằng một hạt cần phải được tiêm. Trong lần quét thứ hai, Spring tìm kiếm một loại đậu MovieFindervà nếu tìm thấy loại đậu đó, nó sẽ tiêm nó vào phương thức này. Nếu nó tìm thấy hai hạt như vậy bạn sẽ nhận được một Exception. Để tránh Exception, bạn có thể sử dụng @Qualifierchú thích và cho biết loại đậu nào trong hai loại đậu được tiêm theo cách sau:

@Qualifier("redBean")
class Red implements Color {
   // Class code here
}

@Qualifier("blueBean")
class Blue implements Color {
   // Class code here
}

Hoặc nếu bạn muốn khai báo các bean trong XML của mình, nó sẽ trông giống như thế này:

<bean id="redBean" class="com.mycompany.movies.Red"/>

<bean id="blueBean" class="com.mycompany.movies.Blue"/>

Trong phần @Autowiredkhai báo, bạn cũng cần thêm phần @Qualifierđể cho biết loại nào trong hai loại đậu màu cần tiêm:

@Autowired
@Qualifier("redBean")
public void setColor(Color color) {
  this.color = color;
}

Nếu bạn không muốn sử dụng hai chú thích ( @Autowired@Qualifier), bạn có thể sử dụng @Resourceđể kết hợp hai chú thích này:

@Resource(name="redBean")
public void setColor(Color color) {
  this.color = color;
}

Các @Resource(bạn có thể đọc một số dữ liệu thêm về nó trong các bình luận đầu tiên về câu trả lời này) phụ tùng bạn việc sử dụng hai chú thích và thay vào đó bạn chỉ sử dụng một.

Tôi sẽ chỉ thêm hai bình luận nữa:

  1. Thực hành tốt sẽ được sử dụng @Injectthay @Autowiredvì bởi vì nó không phải là đặc trưng của mùa xuân và là một phần của JSR-330tiêu chuẩn .
  2. Một cách thực hành tốt khác là đặt @Inject/ @Autowiredtrên một hàm tạo thay vì một phương thức. Nếu bạn đặt nó trên một hàm tạo, bạn có thể xác nhận rằng các hạt được tiêm không phải là null và thất bại nhanh khi bạn cố gắng khởi động ứng dụng và tránh NullPointerExceptionkhi bạn thực sự cần sử dụng bean.

Cập nhật : Để hoàn thành bức tranh, tôi đã tạo một câu hỏi mới về @Configurationlớp.


6
Chỉ để hoàn thành câu trả lời tuyệt vời của bạn: '@Resource' là một phần của tiêu chuẩn JSR-250 và có ngữ nghĩa bổ sung trên và tiêm đơn giản (Như bạn đã nói '@Autowired' là từ Spring; và '@Inject' là một phần của JSR-330) :)
Ignacio Rubio

Nếu MovieFinderlà Giao diện và chúng ta có một bean cho MovieFinderImpl(bean id = movie Downloader), Spring sẽ tự động tiêm nó theo loại hoặc theo tên?
Jaskey 8/8/2015

@jaskey - nó phụ thuộc vào việc bạn sử dụng @Qualifier. Nếu bạn làm - theo tên, nếu không - theo loại. Theo loại sẽ chỉ hoạt động nếu bạn chỉ có một loại đậu MovieFindertrong ngữ cảnh của bạn. Nhiều hơn 1 sẽ dẫn đến một ngoại lệ.
Avi

@Avi, câu trả lời tuyệt vời. Nhưng tôi không hiểu cách @Autowiredchú thích hoạt động trên preparephương thức trong Ví dụ 2 . Nó đang khởi tạo MovieRecommendernhưng, về mặt kỹ thuật, nó KHÔNG phải là một setter.
Karan Chadha

@KaranChadha - @AutowiredCũng hoạt động cho các nhà xây dựng. Nó tìm thấy các phụ thuộc cần thiết và tiêm chúng vào hàm tạo.
Avi

21

Không có gì trong ví dụ nói rằng "các lớp thực hiện cùng một giao diện". MovieCataloglà một loại và CustomerPreferenceDaolà một loại khác. Mùa xuân có thể dễ dàng phân biệt chúng.

Trong Spring 2.x, việc nối dây của đậu chủ yếu xảy ra thông qua ID hoặc tên của bean. Điều này vẫn được hỗ trợ bởi Spring 3.x nhưng thông thường, bạn sẽ có một phiên bản của một bean với một loại nhất định - hầu hết các dịch vụ đều là singletons. Tạo tên cho những người là tẻ nhạt. Vì vậy, Spring bắt đầu hỗ trợ "autowire theo loại".

Những gì các ví dụ cho thấy là nhiều cách khác nhau mà bạn có thể sử dụng để tiêm đậu vào các trường, phương thức và hàm tạo.

XML đã chứa tất cả thông tin mà Spring cần vì bạn phải chỉ định tên lớp đủ điều kiện trong mỗi bean. Bạn cần cẩn thận một chút với các giao diện, mặc dù:

Điều này tự động sẽ thất bại:

 @Autowired
 public void prepare( Interface1 bean1, Interface1 bean2 ) { ... }

Vì Java không giữ tên tham số trong mã byte, Spring không thể phân biệt giữa hai hạt nữa. Cách khắc phục là sử dụng @Qualifier:

 @Autowired
 public void prepare( @Qualifier("bean1") Interface1 bean1,
     @Qualifier("bean2")  Interface1 bean2 ) { ... }

@AaronDigulla Thật tuyệt. Tuy nhiên tôi muốn biết làm thế nào để bạn gọi hàm prepare, tham số nào sẽ được sử dụng để gọi hàm này?
Nguyễn Quang Anh

@NguyenQuangAnh Tôi không gọi phương thức, Spring sẽ làm điều đó khi bean được tạo. Điều này xảy ra chính xác khi @Autowiredcác trường được tiêm. Spring sau đó sẽ thấy rằng các tham số là cần thiết và nó sẽ sử dụng các quy tắc tương tự được sử dụng để tiêm trường để tìm các tham số.
Aaron Digulla

5

Có, bạn có thể định cấu hình tệp xml bối cảnh Spring servlet để xác định các bean của bạn (tức là các lớp), để nó có thể tự động tiêm cho bạn. Tuy nhiên, xin lưu ý rằng bạn phải thực hiện các cấu hình khác để có Spring up và chạy và cách tốt nhất để làm điều đó, là làm theo hướng dẫn cơ bản.

Khi bạn đã cấu hình Spring của mình, bạn có thể thực hiện các thao tác sau trong tệp xml bối cảnh Spring servlet cho Ví dụ 1 ở trên để hoạt động (vui lòng thay thế tên gói của com.movies thành tên gói thực sự và nếu đây là bên thứ 3 lớp, sau đó hãy chắc chắn rằng tệp jar thích hợp nằm trên đường dẫn lớp):

<beans:bean id="movieFinder" class="com.movies.MovieFinder" />

hoặc nếu lớp Movie Downloader có một hàm tạo có giá trị nguyên thủy, thì bạn có thể tạo một cái gì đó như thế này,

<beans:bean id="movieFinder" class="com.movies.MovieFinder" >
    <beans:constructor-arg value="100" />
</beans:bean>

hoặc nếu lớp Movie Downloader có một hàm tạo mong đợi một lớp khác, thì bạn có thể làm một cái gì đó như thế này,

<beans:bean id="movieFinder" class="com.movies.MovieFinder" >
    <beans:constructor-arg ref="otherBeanRef" />
</beans:bean>

... Trong đó ' otherBeanRef ' là một bean khác có tham chiếu đến lớp dự kiến.


4
Việc xác định tất cả các hệ thống dây trong XML chỉ bỏ lỡ toàn bộ ý tưởng về@Autowired
Avi
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.