Những lớp nào sẽ được tự động bởi Spring (khi nào nên sử dụng phép nội xạ phụ thuộc)?


32

Bây giờ tôi đã sử dụng Dependency Injection vào mùa xuân và tôi hiểu cách thức hoạt động của nó và một số ưu và nhược điểm của việc sử dụng nó. Tuy nhiên, khi tôi tạo một lớp mới, tôi thường tự hỏi - Lớp này có được quản lý bởi Spring IOC Container không?

Và tôi không muốn nói về sự khác biệt giữa chú thích @Autowired, cấu hình XML, tiêm setter, tiêm constructor, v.v. Câu hỏi của tôi là một câu hỏi chung.

Giả sử chúng ta có Dịch vụ với Trình chuyển đổi:

@Service
public class Service {

    @Autowired
    private Repository repository;

    @Autowired
    private Converter converter;

    public List<CarDto> getAllCars() {
        List<Car> cars = repository.findAll();
        return converter.mapToDto(cars);
    }
}

@Component
public class Converter {

    public CarDto mapToDto(List<Car> cars) {
        return new ArrayList<CarDto>(); // do the mapping here
    }
}

Rõ ràng, trình chuyển đổi không có bất kỳ sự phụ thuộc nào, do đó không cần thiết phải tự động chuyển đổi. Nhưng đối với tôi nó có vẻ tốt hơn như tự động. Mã sạch hơn và dễ kiểm tra. Nếu tôi viết mã này mà không có DI, dịch vụ sẽ trông như thế:

@Service
public class Service {

    @Autowired
    private Repository repository;

    public List<CarDto> getAllCars() {
        List<Car> cars = repository.findAll();
        Converter converter = new Converter();
        return converter.mapToDto(cars);
    }
}

Bây giờ khó khăn hơn nhiều để kiểm tra nó. Hơn nữa, bộ chuyển đổi mới sẽ được tạo cho mọi hoạt động chuyển đổi, mặc dù nó luôn ở cùng một trạng thái, có vẻ như là một chi phí hoạt động.

Có một số mẫu nổi tiếng trong Spring MVC: Bộ điều khiển sử dụng Dịch vụ và Dịch vụ sử dụng Kho lưu trữ. Sau đó, nếu Kho lưu trữ được tự động (thường là vậy), thì Dịch vụ cũng phải được tự động. Và điều này khá rõ ràng. Nhưng khi nào chúng ta sử dụng chú thích @Component? Nếu bạn có một số lớp sử dụng tĩnh (như trình chuyển đổi, trình ánh xạ) - bạn có tự động xác nhận chúng không?

Bạn có cố gắng để làm cho tất cả các lớp tự động? Sau đó, tất cả các phụ thuộc lớp đều dễ tiêm (một lần nữa, dễ hiểu và dễ kiểm tra). Hay bạn chỉ cố gắng tự động khi nó thực sự cần thiết?

Tôi đã dành một chút thời gian để tìm kiếm một số quy tắc chung về thời điểm sử dụng tính năng tự động, nhưng tôi không thể tìm thấy bất kỳ lời khuyên cụ thể nào. Thông thường, mọi người nói về "bạn có sử dụng DI? (Có / không)" hoặc "bạn thích loại tiêm phụ thuộc nào" không trả lời câu hỏi của tôi.

Tôi sẽ biết ơn bất kỳ lời khuyên liên quan đến chủ đề này!


3
+1 về cơ bản "Khi nào nó đi quá xa?"
Andy Hunt

Kiểm tra câu hỏi này và bài viết này
Laiv

Câu trả lời:


8

Đồng ý với nhận xét của @ ericW và chỉ muốn thêm ghi nhớ, bạn có thể sử dụng trình khởi tạo để giữ cho mã của mình nhỏ gọn:

@Autowired
private Converter converter;

hoặc là

private Converter converter = new Converter();

hoặc, nếu lớp thực sự không có trạng thái nào cả

private static final Converter CONVERTER = new Converter();

Một trong những tiêu chí quan trọng cho việc Spring có nên khởi tạo và tiêm một hạt đậu hay không, đó có phải là loại đậu phức tạp đến mức bạn muốn thử nghiệm nó không? Nếu vậy, sau đó tiêm nó. Ví dụ: nếu bộ chuyển đổi thực hiện một chuyến đi khứ hồi tới bất kỳ hệ thống bên ngoài nào, thì hãy biến nó thành một thành phần thay thế. Hoặc nếu giá trị trả về có một cây quyết định lớn với hàng tá biến thể có thể dựa trên đầu vào, thì hãy giả định nó. Và kể từ đó trở đi.

Bạn đã hoàn thành tốt công việc triển khai chức năng đó và gói gọn nó, vì vậy bây giờ chỉ còn là câu hỏi liệu nó có đủ phức tạp để được coi là một "đơn vị" riêng biệt để thử nghiệm hay không.


5

Tôi không nghĩ rằng bạn phải @Autowired tất cả các lớp của mình, điều đó nên phụ thuộc vào cách sử dụng thực tế, đối với các kịch bản của bạn, nên sử dụng phương thức tĩnh tốt hơn thay vì @Autowired. Tôi chưa thấy sử dụng lợi ích @Autowired cho các lớp utils đơn giản đó và điều đó sẽ hoàn toàn làm tăng chi phí container Spring nếu không sử dụng đúng cách.


2

Quy tắc ngón tay cái của tôi dựa trên một cái gì đó mà bạn đã nói: khả năng kiểm tra. Hãy tự hỏi mình " Tôi có thể kiểm tra đơn vị dễ dàng không? ". Nếu câu trả lời là có thì nếu không có lý do nào khác tôi sẽ ổn với nó. Vì vậy, nếu bạn phát triển bài kiểm tra đơn vị cùng lúc bạn đang phát triển, bạn sẽ tiết kiệm được rất nhiều nỗi đau.

Vấn đề tiềm năng duy nhất là nếu trình chuyển đổi không thành công, kiểm tra dịch vụ của bạn cũng sẽ thất bại. Một số người sẽ nói rằng bạn nên chế giễu kết quả của trình chuyển đổi trong các bài kiểm tra đơn vị. Bằng cách này, bạn sẽ có thể xác định lỗi nhanh. Nhưng nó có một mức giá: bạn phải chế giễu tất cả các kết quả của bộ chuyển đổi khi trình chuyển đổi thực sự có thể đã thực hiện công việc.

Tôi cũng cho rằng không có lý do nào để sử dụng các bộ chuyển đổi dto khác nhau.


0

TL; DR: Một cách tiếp cận kết hợp tự động cho DI và chuyển tiếp hàm tạo cho DI có thể đơn giản hóa mã bạn đã trình bày.

Tôi đã xem xét các câu hỏi tương tự do một số weblogic với các lỗi / biến chứng khởi động khung mùa xuân liên quan đến các phụ thuộc khởi tạo bean @autowired. Tôi đã bắt đầu pha trộn trong một cách tiếp cận DI khác: chuyển tiếp xây dựng. Nó đòi hỏi các điều kiện như bạn trình bày ("Rõ ràng, trình chuyển đổi không có bất kỳ sự phụ thuộc nào, vì vậy không cần thiết phải tự động."). Tuy nhiên, tôi thích rất nhiều cho sự linh hoạt và nó vẫn khá dễ dàng.

@Service
public class Service {

    @Autowired
    private Repository repository;

    public List<CarDto> getAllCars(Converter converter) {
        List<Car> cars = repository.findAll();
        return converter.mapToDto(cars);
    }
    public List<CarDto> getAllCars() {
        Converter converter = new Converter();
        return getAllCars(converter);
    }
}

hoặc thậm chí là một khẩu súng trường câu trả lời của Rob

@Service
public class Service {

    @Autowired
    private Repository repository;

    private final Converter converter = new Converter(); // static if safe for that

    public List<CarDto> getAllCars(Converter converter) {
        List<Car> cars = repository.findAll();
        return converter.mapToDto(cars);
    }
    public List<CarDto> getAllCars() {
        return getAllCars(converter);
    }
}

Nó có thể không yêu cầu thay đổi giao diện công cộng nhưng tôi sẽ làm vậy. Vẫn là

public List<CarDto> getAllCars(Converter converter) { ... }

có thể được bảo vệ hoặc riêng tư để giảm phạm vi cho mục đích thử nghiệm / mở rộng.

Điểm quan trọng là DI là tùy chọn. Một mặc định được cung cấp, có thể được ghi đè theo cách thẳng về phía trước. Nó có điểm yếu khi số lượng trường tăng, nhưng với 1, có thể 2 trường, tôi thích điều này trong các điều kiện sau:

  • Số lượng trường rất ít
  • Mặc định hầu như luôn được sử dụng (DI là đường nối thử nghiệm) hoặc Mặc định gần như không bao giờ được sử dụng (dừng khoảng cách) vì tôi thích tính nhất quán, vì vậy đây là một phần của cây quyết định của tôi, không phải là một ràng buộc thiết kế thực sự

Điểm cuối cùng (một chút OT, nhưng liên quan đến cách chúng tôi quyết định cái gì / ở đâu @autowired): lớp trình chuyển đổi, như được trình bày, là phương thức tiện ích (không có trường, không có hàm tạo, có thể là tĩnh). Có lẽ giải pháp sẽ có một số loại phương thức mapToDto () trong lớp Cars? Đó là, đẩy nội dung chuyển đổi sang định nghĩa Ô tô, nơi có khả năng bị ràng buộc chặt chẽ:

@Service
public class Service {

   @Autowired
   private Repository repository;

   public List<CarDto> getAllCars() {
    return repository.findAll().stream.map(c -> c.mapToDto()).collect(Collectors.toList()));
   }
}

-1

Tôi nghĩ một ví dụ tốt về điều này là một cái gì đó giống như SecurityUtils hoặc UserUtils. Với bất kỳ ứng dụng Spring nào quản lý người dùng, bạn sẽ cần một số lớp Util với toàn bộ các phương thức tĩnh như:

getCienUser ()

được xác nhận ()

isCienUserInRole (Cơ quan có thẩm quyền)

v.v.

và tôi chưa bao giờ tự động những thứ này. JHipster (mà tôi sử dụng như một thẩm phán khá giỏi về thực hành tốt nhất) thì không.


-2

Chúng ta có thể tách các lớp trên cơ sở chức năng của lớp đó như bộ điều khiển, dịch vụ, kho lưu trữ, thực thể. Chúng tôi có thể sử dụng các lớp khác cho logic của chúng tôi không chỉ cho mục đích của bộ điều khiển, dịch vụ, v.v., nhân dịp đó, chúng tôi có thể chú thích các lớp đó với @component. Điều này sẽ tự động đăng ký lớp đó trong container mùa xuân. Nếu nó được đăng ký thì nó sẽ được quản lý bởi container mùa xuân. Khái niệm về tiêm phụ thuộc và đảo ngược kiểm soát có thể được cung cấp cho lớp chú thích đó.


3
bài này khá khó đọc (tường văn bản). Bạn có phiền chỉnh sửa ing nó thành một hình dạng tốt hơn?
gnat
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.