IT

복수의 실장이 있는 인터페이스를 자동 접속하는 스프링 부트

itgroup 2023. 4. 3. 21:26
반응형

복수의 실장이 있는 인터페이스를 자동 접속하는 스프링 부트

통상의 봄에서는, 인터페이스를 자동 접속하는 경우는, Spring 콘텍스트파일로 그 실장을 정의합니다.

  1. 스프링 부츠는 어때?
  2. 우리가 어떻게 이것을 달성할 수 있을까?

현재 인터페이스가 아닌 클래스만 자동 배선하고 있습니다.

이 질문의 또 다른 부분은 스프링 부트프로젝트 내의 Junit 클래스의 클래스 사용에 관한 것입니다.

예를 들어 Calendar Util을 사용하는 경우 Calendar Util을 자동 연결하면 늘 포인터 예외가 발생합니다.이 경우 우리가 할 수 있는 일은 무엇인가?지금은 "new"로 초기화했습니다.

사용하다@Qualifier주석은 동일한 인터페이스의 콩을 구별하기 위해 사용됩니다.
Spring Boot 매뉴얼을 참조하십시오.
또, 같은 인터페이스의 모든 콩을 주입하려면 , 자동 배선만 하면 됩니다. List인터페이스의
(스프링/스프링 부트/스프링 부트 테스트에서도 동일)
다음 예:

@SpringBootApplication
public class DemoApplication {

public static void main(String[] args) {
    SpringApplication.run(DemoApplication.class, args);
}

public interface MyService {

    void doWork();

}

@Service
@Qualifier("firstService")
public static class FirstServiceImpl implements MyService {

    @Override
    public void doWork() {
        System.out.println("firstService work");
    }

}

@Service
@Qualifier("secondService")
public static class SecondServiceImpl implements MyService {

    @Override
    public void doWork() {
        System.out.println("secondService work");
    }

}

@Component
public static class FirstManager {

    private final MyService myService;

    @Autowired // inject FirstServiceImpl
    public FirstManager(@Qualifier("firstService") MyService myService) {
        this.myService = myService;
    }

    @PostConstruct
    public void startWork() {
        System.out.println("firstManager start work");
        myService.doWork();
    }

}

@Component
public static class SecondManager {

    private final List<MyService> myServices;

    @Autowired // inject MyService all implementations
    public SecondManager(List<MyService> myServices) {
        this.myServices = myServices;
    }

    @PostConstruct
    public void startWork() {
        System.out.println("secondManager start work");
        myServices.forEach(MyService::doWork);
    }

}

}

질문의 두 번째 부분에서는 먼저 이 유용한 답변살펴보십시오.

구현 이름을 지정하여 작업을 수행할 수도 있습니다.

예:

@Autowired
MyService firstService;

@Autowired
MyService secondService;

다음 명령어가 있다고 가정합니다.GreetingService

public interface GreetingService {
    void doGreetings();
}

2개의 구현이 있습니다.HelloService

@Service
@Slf4j
public class HelloService implements GreetingService{
    @Override
    public void doGreetings() {
        log.info("Hello world!");       
    }
}

그리고.HiService

@Slf4j
@Service
public class HiService implements GreetingService{
    @Override
    public void doGreetings() {
        log.info("Hi world!");
    }
}

또 다른 인터페이스가 있습니다.즉,BusinessService용건을 묻다

public interface BusinessService {
    void doGreetings();
}

그렇게 하는 방법에는 몇 가지 있다

#1. 사용@Autowired

@Component
public class BusinessServiceImpl implements BusinessService{
    
    @Autowired
    private GreetingService hiService; // Spring automatically maps the name for you, if you don't want to change it.
    
    @Autowired
    private GreetingService helloService;
    

    @Override
    public void doGreetings() {
        hiService.doGreetings();
        helloService.doGreetings();
    }

}

구현 빈 이름을 변경해야 할 경우 예를 들어 이름을 빈으로 설정하여 다른 답변을 참조하십시오.@Service("myCustomName")및 적용@Qualifier("myCustomName")

#2. 컨스트럭터 주입을 사용할 수도 있습니다.

@Component
public class BusinessServiceImpl implements BusinessService {

    private final GreetingService hiService;

    private final GreetingService helloService;

    public BusinessServiceImpl(GreetingService hiService, GreetingService helloService) {
        this.hiService = hiService;
        this.helloService = helloService;
    }

    @Override
    public void doGreetings() {
        hiService.doGreetings();
        helloService.doGreetings();
    }

}

이 경우

public BusinessServiceImpl(@Qualifier("hiService") GreetingService hiService, @Qualifier("helloService") GreetingService helloService)

단, 스프링 부츠를 사용하고 있습니다.2.6.5그리고.

public BusinessServiceImpl(GreetingService hiService, GreetingService helloService)

잘 되고 있어요.스프링 씨가 자동으로 이름을 불러주니까

#3. 또,Map이 때문에

@Component
@RequiredArgsConstructor
public class BusinessServiceImpl implements BusinessService {

    private final Map<String, GreetingService> servicesMap; // Spring automatically get the bean name as key

    @Override
    public void doGreetings() {
        servicesMap.get("hiService").doGreetings();
        servicesMap.get("helloService").doGreetings();
    }

}

List모든 서비스를 실행해도 정상적으로 동작합니다.다만, 특정의 실장을 필요로 하는 경우는, 실장의 이름을 정의할 필요가 있습니다.참고 자료는 여기 있습니다.

이번 건은 제가@RequiredArgsConstructor롬복에서.

댓글에 기재되어 있듯이@Qualifier주석: 문서에서 설명한 대로 다양한 구현을 구별할 수 있습니다.

테스트의 경우 를 사용하여 동일한 작업을 수행할 수도 있습니다.예를 들어 다음과 같습니다.

    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class MyClassTests {

        @Autowired
        private MyClass testClass;
        @MockBean
        @Qualifier("default")
        private MyImplementation defaultImpl;

        @Test
        public void givenMultipleImpl_whenAutowiring_thenReturnDefaultImpl() {
    // your test here....
    }
}

복수의 실장 인터페이스를 자동 접속하는 경우는, 다음의 2개의 어프로치가 있습니다.

  1. Spring @Primary 주석

즉, @Primary 주석으로 표시된 특정 구현을 사용하도록 인터페이스를 자동 연결하려고 할 때마다 Spring 애플리케이션에 알립니다.이것은 디폴트 자동 배선 설정과 같습니다.인터페이스의 실장 클러스터별로 1회만 사용할 수 있습니다. @Primary Docs

  1. 스프링 @Qualifier 주석

이번 봄의 주석을 통해 옵션 중 선택하는 인터페이스에 대한 참조를 정의하는 모든 장소에서 정확한 구현을 선택할 수 있습니다. @Qualifier 문서

상세한 것에 대하여는, 메뉴얼의 링크를 참조해 주세요.

public interface SomeInterfaces {

    void send(String message);

    String getType();
}
  • 카프카 서비스
@Component
public class SomeInterfacesKafkaImpl implements SomeInterfaces {

    private final String type = "kafka";

    @Override
    public void send(String message) {
        System.out.println(message + "through Kafka");
    }

    @Override
    public String getType() {
        return this.type;
    }
}
  • 리디스 서비스
@Component
public class SomeInterfacesRedisImpl implements SomeInterfaces {

    private final String type = "redis";

    @Override
    public void send(String message) {
        System.out.println(message + "through Redis");
    }

    @Override
    public String getType() {
        return this.type;
    }
}
  • 마스터.
@Component
public class SomeInterfacesMaster {

    private final Set<SomeInterfaces> someInterfaces;


    public SomeInterfacesMaster(Set<SomeInterfaces> someInterfaces) {
        this.someInterfaces = someInterfaces;
    }

    public void sendMaster(String type){

        Optional<SomeInterfaces> service =
                someInterfaces
                        .stream()
                        .filter(service ->
                                service.getType().equals(type)
                        )
                        .findFirst();
        SomeInterfaces someService =
                service
                        .orElseThrow(() -> new RuntimeException("There is not such way for sending messages."));

        someService .send(" Hello. It is a letter to ....");

    }
}
  • 시험

@SpringBootTest
public class MultiImplementation {
}

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class SomeInterfacesMasterTest extends MultiImplementation {

    @Autowired
    private SomeInterfacesMaster someInterfacesMaster;

    @Test
    void sendMaster() {
        someInterfacesMaster.sendMaster("kafka");
    }
}

따라서 Open/Closed 원칙에 따라 기존 코드를 깨지 않고 구현만 추가하면 됩니다.

@Component
public class SomeInterfacesRabbitImpl implements SomeInterfaces {

   private final String type = "rabbit";

    @Override
    public void send(String message) {
        System.out.println(message + "through Rabbit");
    }

    @Override
    public String getType() {
        return this.type;
    }
}

  • 테스트 v2
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class SomeInterfacesMasterTestV2 extends MultiImplementation {

    @Autowired
    private SomeInterfacesMaster someInterfacesMaster;


    @Test
    void sendMasterV2() {
        someInterfacesMaster.sendMaster("rabbit");
    }
}

같은 인터페이스를 여러실장하고 있는 경우, Spring은 클래스에 자동 접속할 필요가 있는 인터페이스를 알아야 합니다.종업원의 휴대 전화 번호이메일 주소의 간단한 예를 다음에 나타냅니다.-

종업원 클래스:

public class Employee {
    
     private String mobileNumber;
     private String emailAddress;
     
     ...
     
     /** Getters & Setters omitted **/
}

인터페이스 Employee Validator:

public interface EmployeeValidator {
    public Employee validate(Employee employee);
}

모바일 번호 검증 도구의 첫 번째 구현 클래스:

@Component(value="EmployeeMobileValidator")
public class EmployeeMobileValidator implements EmployeeValidator {
    @Override
    public Employee validate(Employee employee) {
        //Mobile number Validation logic goes here.
    }
}

이메일 주소 검증기의 두 번째 구현 클래스:

@Component(value="EmployeeEmailValidator")
public class EmployeeEmailValidator implements EmployeeValidator {
    @Override
    public Employee validate(Employee employee) {
        //Email address validation logic goes here.
    }
}

이제 의 검증자를 개별적으로 클래스에 자동 연결할 수 있습니다.

직원 서비스 인터페이스:

public interface EmployeeService {
    public void handleEmployee(Employee employee);
}

직원 서비스 구현 클래스

@Service
public class EmployeeServiceImpl implements EmployeeService {
    
    /** Autowire validators individually **/
    
    @Autowired
    @Qualifier("EmployeeMobileValidator")    // Autowired using qualifier for mobile validator
    private EmployeeValidator mobileValidator;

    @Autowired
    @Qualifier("EmployeeEmailValidator")    // Autowired using qualifier for email valodator
    private EmployeeValidator emailValidator;

    @Override
    public void handleEmployee(Employee employee) {
    
        /**You can use just one instance if you need**/
        
        employee = mobileValidator.validate(employee);        
        
        
    }   
}

언급URL : https://stackoverflow.com/questions/51766013/spring-boot-autowiring-an-interface-with-multiple-implementations

반응형