Spring에 의해 어떤 클래스가 자동 연결되어야합니까 (종속성 주입을 사용하는 경우)?


32

나는 Spring에서 Dependency Injection을 한동안 사용 해 왔으며 그것이 어떻게 작동하고 그것을 사용하는 장단점이 무엇인지 이해합니다. 그러나 새 클래스를 만들 때 종종 궁금합니다.이 클래스는 Spring IOC 컨테이너에서 관리해야합니까?

그리고 @Autowired 주석, XML 구성, setter 주입, 생성자 주입 등의 차이점에 대해 이야기하고 싶지 않습니다. 내 질문은 일반적인 질문입니다.

변환기가있는 서비스가 있다고 가정 해 보겠습니다.

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

분명히 변환기에는 종속성이 없으므로 자동 배선 할 필요가 없습니다. 그러나 나를 위해 그것은 자동 유선으로 더 나은 것 같습니다. 코드는 더 깨끗하고 테스트하기 쉽습니다. DI 없이이 코드를 작성하면 서비스는 다음과 같습니다.

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

이제 테스트하기가 훨씬 어렵습니다. 또한 항상 같은 상태에 있어도 오버 헤드처럼 보이는 모든 변환 작업에 대해 새 변환기가 만들어집니다.

Spring MVC : 저장소를 사용하는 서비스 및 서비스를 사용하는 컨트롤러에는 잘 알려진 패턴이 있습니다. 그런 다음 리포지토리가 자동 연결되어 있으면 (일반적으로) 서비스도 자동 연결되어야합니다. 그리고 이것은 매우 분명합니다. 그러나 @Component 주석은 언제 사용합니까? 정적 유틸리티 클래스 (예 : 변환기, 매퍼)가있는 경우 자동 와이어 링합니까?

모든 수업을 자동으로 연결하려고하십니까? 그런 다음 모든 클래스 종속성을 주입하기 쉽습니다 (다시 한 번 이해하고 테스트하기 쉬움). 아니면 꼭 필요한 경우에만 자동 와이어 링을 시도합니까?

자동 배선 사용시기에 대한 몇 가지 일반적인 규칙을 찾는 데 시간을 보냈지 만 구체적인 팁을 찾을 수 없었습니다. 일반적으로 사람들은 "DI를 사용합니까? (예 / 아니오)"또는 "어떤 종류의 의존성 주입을 선호합니까"에 대해 이야기하는데, 제 질문에 대답하지 않습니다.

이 주제에 관한 팁에 감사드립니다!


3
본질적으로 "언제 지나가는가?"+1
Andy Hunt

질문 과이 기사를
Laiv

답변:


8

@ericW의 의견에 동의하고 초기화 프로그램을 사용하여 코드를 간결하게 유지할 수 있음을 기억하십시오.

@Autowired
private Converter converter;

또는

private Converter converter = new Converter();

또는 수업에 실제로 상태가없는 경우

private static final Converter CONVERTER = new Converter();

Spring이 bean을 인스턴스화하고 주입해야하는지에 대한 핵심 기준 중 하나는 bean이 너무 복잡하여 테스트에서 조롱하고 싶습니까? 그렇다면 주입하십시오. 예를 들어, 변환기가 외부 시스템을 왕복하는 경우 대신 구성 요소로 만드십시오. 또는 반환 값에 입력을 기반으로 수십 가지 변형이 가능한 큰 결정 트리가있는 경우이를 조롱하십시오. 기타 등등.

이미 해당 기능을 롤업하고 캡슐화하는 작업을 완료 했으므로 이제는 테스트를 위해 별도의 "단위"로 간주하기에 복잡한 지 여부에 대한 문제 일뿐입니다.


5

모든 클래스를 @Autowired 할 필요는 없다고 생각합니다. 실제 사용법에 달려 있어야합니다. 시나리오의 경우 @Autowired 대신 정적 메소드를 사용하는 것이 좋습니다. 간단한 유틸리티 클래스에 @Autowired를 사용하는 이점을 보지 못했으며 올바르게 사용하지 않으면 스프링 컨테이너 비용이 절대적으로 증가합니다.


2

내 경험 법칙은 당신이 이미 말한 것 : 테스트 가능성에 근거합니다. " 나는 쉽게 단위 테스트를 할 수 있습니까? " 대답이 '예'라면 다른 이유가 없으면 괜찮습니다. 따라서 단위 테스트를 개발하는 동시에 개발하는 동안 많은 고통을 줄일 수 있습니다.

유일한 잠재적 인 문제는 변환기가 실패하면 서비스 테스트도 실패한다는 것입니다. 어떤 사람들은 단위 테스트에서 변환기의 결과를 조롱해야한다고 말합니다. 이렇게하면 오류를 더 빨리 식별 할 수 있습니다. 그러나 가격이 있습니다 : 실제 변환기가 작업을 수행 할 수있을 때 모든 변환기 결과를 조롱해야합니다.

또한 다른 dto 변환기를 사용할 이유가 전혀 없다고 가정합니다.


0

TL; DR : DI에 대한 자동 배선 및 DI에 대한 생성자 전달의 하이브리드 방식은 제시 한 코드를 단순화 할 수 있습니다.

@autowired bean 초기화 종속성과 관련된 스프링 프레임 워크 시작 오류 / 합병증이있는 weblogic으로 인해 비슷한 질문을 보았습니다. 다른 DI 접근법 인 생성자 전달에서 믹싱을 시작했습니다. 현재 상태와 같은 조건이 필요합니다 ( "컨버터에는 종속성이 없으므로 자동 배선 할 필요가 없습니다"). 그럼에도 불구하고, 나는 유연성에 대해 많은 것을 좋아하고 여전히 간단합니다.

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

심지어 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);
    }
}

공용 인터페이스 변경이 필요하지는 않지만 필요합니다. 여전히

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

테스트 목적 / 확장 목적으로 만 범위를 제한하기 위해 보호되거나 개인화 될 수 있습니다.

요점은 DI는 선택 사항입니다. 기본적으로 제공되며, 이는 곧바로 재정의 될 수 있습니다. 필드 수가 증가함에 따라 약점이 있지만 1 개, 2 개 필드의 경우 다음 조건에서 이것을 선호합니다.

  • 매우 적은 수의 필드
  • 기본값은 거의 항상 사용됩니다 (DI는 테스트 솔기) 또는 기본값은 거의 사용되지 않습니다 (간격 멈춤) 일관성을 좋아하기 때문에 이것은 진정한 디자인 제약이 아닌 의사 결정 트리의 일부입니다

마지막 요점 (비트 OT이지만 우리가 무엇을 어디에서 / autowired로 결정하는지와 관련이 있습니다) : 제시된 변환기 클래스는 유틸리티 메소드입니다 (필드가없고 생성자가 없으며 정적 일 수 있음). 아마도 솔루션은 Cars 클래스에 일종의 mapToDto () 메소드를 가지고 있습니까? 즉, 전환 주입을 Cars 정의로 푸시하십시오.

@Service
public class Service {

   @Autowired
   private Repository repository;

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

-1

좋은 예는 SecurityUtils 또는 UserUtils와 같은 것입니다. 사용자를 관리하는 Spring 앱을 사용하려면 다음과 같은 정적 메소드가 많은 Util 클래스가 필요합니다.

getCurrentUser ()

isAuthenticated ()

isCurrentUserInRole (권한 기관)

기타

그리고 나는 이것을 자동 배선하지 않았습니다. JHipster (내가 모범 사례의 훌륭한 판단으로 사용)는 그렇지 않습니다.


-2

컨트롤러, 서비스, 저장소, 엔터티가되도록 해당 클래스의 기능에 따라 클래스를 분리 할 수 ​​있습니다. 우리는 컨트롤러, 서비스 등의 목적이 아닌 논리에 다른 클래스를 사용할 수 있습니다. 그러면 스프링 컨테이너에 해당 클래스가 자동으로 등록됩니다. 등록 된 경우 스프링 컨테이너로 관리됩니다. 의존성 주입과 제어 역전의 개념은 주석이 달린 클래스에 제공 될 수 있습니다.


3
이 게시물은 읽기 어렵습니다 (텍스트의 벽). 더 나은 형태로 편집 하시겠습니까 ?
gnat
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.