Spring tarafından hangi sınıflar otomatik olarak bağlanmalıdır (bağımlılık enjeksiyonunu ne zaman kullanmalı)?


32

İlkbaharda Bağımlılık Enjeksiyonunu bir süredir kullanıyorum ve nasıl çalıştığını ve kullanmanın bazı avantaj ve dezavantajlarının ne olduğunu biliyorum. Ancak, yeni bir sınıf oluştururken sık sık merak ediyorum - Bu sınıf Spring IOC Container tarafından yönetilmeli mi?

Ve @ Otomatik ek açıklama, XML konfigürasyonu, setter enjeksiyonu, yapıcı enjeksiyonu vb. Arasındaki farklar hakkında konuşmak istemiyorum. Sorum geneldir.

Diyelim ki Dönüştürücülü bir Hizmetimiz var:

@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
    }
}

Açıkçası, dönüştürücünün herhangi bir bağımlılığı yoktur, dolayısıyla otomatik olarak bağlanması gerekli değildir. Ama benim için otomatik olarak daha iyi görünüyor. Kod daha temiz ve test etmesi kolaydır. Bu kodu DI olmadan yazarsam, servis şöyle görünecektir:

@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);
    }
}

Şimdi test etmek çok daha zor. Ayrıca, her zaman bir genel gider gibi gözükse de, her dönüştürme işlemi için yeni dönüştürücü oluşturulacaktır.

Spring MVC'de bilinen bazı desenler var: Depoları kullanarak Servisleri ve Servisleri kullanan kontrolörler. Ardından, Depo otomatik olarak bağlanırsa (genellikle olduğu gibi), Hizmet'in de otomatik olarak bağlanması gerekir. Ve bu oldukça açık. Fakat @Component notunu ne zaman kullanırız? Bazı statik util sınıflarınız varsa (dönüştürücüler, eşleyiciler gibi) - onları otomatik olarak dizdiniz mi?

Tüm sınıfları otomatik olarak kablolu hale getirmeye çalışıyor musunuz? O zaman tüm sınıf bağımlılıklarının enjekte edilmesi kolaydır (bir kez daha, anlaşılması ve test edilmesi kolaydır). Yoksa sadece kesinlikle gerekli olduğunda otomatik olarak çalışmayı deniyor musunuz?

Otomatik kablolamanın ne zaman kullanılacağına ilişkin genel kurallar aramak için biraz zaman harcadım, ancak belirli bir ipucu bulamadım. Genelde insanlar "DI kullanıyor musunuz? (Evet / hayır)" veya "ne tür bir bağımlılık enjeksiyonunu tercih edersiniz" hakkında konuşur, bu da sorumu cevaplamıyor.

Bu konuyla ilgili herhangi bir ipucu için minnettar olurum!


3
Esas olarak +1 "Ne zaman çok uzağa gidiyor?"
Andy Hunt,

Yanıtlar:


8

@ EricW adlı kullanıcının yorumuna katılıyorum ve yalnızca kodunuzu küçük tutmak için başlatıcıları kullanabileceğinizi unutmayın:

@Autowired
private Converter converter;

veya

private Converter converter = new Converter();

veya, eğer sınıf gerçekten hiç bir devlete sahip değilse

private static final Converter CONVERTER = new Converter();

Spring'in bir fasulyeyi somutlaştırması ve enjekte etmesi gerekip gerekmediği konusundaki kilit kriterlerden biri, fasulyeyi denemekle alay etmek istediğiniz kadar karmaşık mı? Eğer öyleyse, o zaman enjekte edin. Örneğin, dönüştürücü herhangi bir harici sisteme gidiş dönüş yaparsa, bunun yerine bir bileşen haline getirin. Ya da geri dönüş değeri, girdiye bağlı olarak düzinelerce olası çeşitlilik gösteren büyük bir karar ağacına sahipse, alayla geçin. Ve bunun gibi.

Zaten bu fonksiyonelliğin üstesinden gelmek ve onu içine almak için iyi bir iş çıkardınız, bu yüzden şimdi test için ayrı bir "ünite" olarak kabul edilebilecek kadar karmaşık olup olmadığı bir soru.


5

Tüm sınıflarınızda @Otomatik hale getirmeniz gerektiğini düşünmüyorum, gerçek kullanıma bağlı olmalı, senaryolarınız için @Autowired yerine statik yöntemi kullanması daha iyi olmalı. Bu basit araçlar sınıfı için @Autowired kullanımının faydasını görmedim ve bu doğru kullanılmadığı takdirde Spring konteyner maliyetini kesinlikle artıracak.


2

Temel kuralm, reddettiğiniz bir şeye dayanıyor: test edilebilirlik. Kendinize " Birimi kolayca test edebilir miyim? " Cevabınız evet ise, başka bir nedenin yokluğunda ben onunla iyi olurdu. Bu nedenle, eğer ünite testini aynı anda geliştirirseniz, geliştirdiğiniz zaman çok fazla acı çekersiniz.

Tek olası sorun, dönüştürücü başarısız olursa, servis testinizin de başarısız olmasıdır. Bazı insanlar, konvertörün sonuçlarını birim testlerinde alay etmeniz gerektiğini söyler. Bu sayede hataları tespit edebileceksiniz. Ancak bunun bir bedeli var: gerçek çevirici işi yaptığında, çeviricinin tüm sonuçlarını alay etmek zorundasınız.

Ayrıca farklı dto Çeviriciler kullanmak için hiçbir neden olmadığını varsayıyorum.


0

TL; DR: DI için otomatik kablolama hibrit yaklaşımı ve DI için yapıcı iletme, sunduğunuz kodu basitleştirebilir.

İlkbahar çerçevesi başlangıç ​​hataları / otomatik fasülye başlatma bağımlılıklarını içeren komplikasyonlar nedeniyle benzer soruları inceledim. Başka bir DI yaklaşımında karıştırmaya başladım: yapıcı yönlendirme. Sizin gibi şartlar gerektiriyor ("Dönüştürücünün açık bir şekilde herhangi bir bağımlılığı yok, bu yüzden otomatik olarak bağlanması gerekli değil"). Yine de, esneklik için çok hoşuma gitti ve hala oldukça açık.

@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);
    }
}

hatta Rob'ın cevabını yırttı

@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);
    }
}

Genel bir arayüz değişikliği gerektirmeyebilir ama ben isterim. Hala

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

sadece test amaçlı / uzatma kapsamına girecek şekilde korunabilir veya özel yapılabilir.

Anahtar nokta DI isteğe bağlı olmasıdır. Düz bir şekilde geçersiz kılınabilir bir varsayılan sağlanır. Alan sayısı arttıkça zayıflığı vardır, ancak 1, belki 2 alan için, aşağıdaki koşullar altında bunu tercih ederim:

  • Çok az sayıda alan
  • Varsayılan, neredeyse her zaman kullanılır (DI bir test dikişidir) veya Varsayılan, neredeyse hiç kullanılmaz (boşluğu durdurur) çünkü tutarlılığı seviyorum, bu yüzden bu karar ağacımın bir parçası, gerçek bir tasarım kısıtı değil

Son nokta (biraz OT, ama ne / nerede @autowired'da neye karar verdiğimize bağlı olarak): dönüştürücü sınıfı, sunulduğu gibi, yardımcı program yöntemidir (alan yok, yapıcı yok, statik olabilir). Belki de çözüm, Cars sınıfında bir çeşit mapToDto () yöntemine sahip olacaktı? Yani, dönüştürme enjeksiyonunu büyük olasılıkla zaten çoktan bağlanmış olan Cars tanımına itin:

@Service
public class Service {

   @Autowired
   private Repository repository;

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

-1

Bunun iyi bir örneği SecurityUtils veya UserUtils gibi bir şey olduğunu düşünüyorum. Kullanıcıları yöneten herhangi bir Spring uygulamasıyla, aşağıdaki gibi statik yöntemlerle dolu bir Util sınıfına ihtiyacınız olacak:

getCurrentUser ()

IsAuthenticated ()

isCurrentUserInRole (Yetkili Makam)

vb.

ve ben bunları asla otomatik olarak bağladım. JHipster (en iyi uygulamaların oldukça iyi bir hakimi olarak kullanıyorum).


-2

Sınıfları, kontrolcü, servis, depo ve varlık gibi sınıfın işlevine göre ayırabiliriz. Diğer sınıfları mantığımız için sadece kontrolörler, servis vb. Amaçlar için değil, bu vesileyle bu sınıfları @ bileşeniyle ekleyebiliriz. Bu, otomatik olarak bu sınıfı yay kabına kaydeder. Kayıtlı ise yaylı konteyner ile yönetilecektir. Bağımlılık enjeksiyonu ve kontrolün tersine çevrilmesi kavramı bu açıklamalı sınıfa sunulabilir.


3
Bu yazı okumak oldukça zordur (metin duvarı). Sakıncası var düzenleyebilir daha iyi bir şekle ing?
tatarcık
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.