
이전에 작성한 자바 클래스 간의 강결합을 느슨하게 바꾸기 포스팅에서는 자바 클래스 간의 강결합을 느슨하게 하는 방법을 살펴보았습니다(서비스의 분리, 의존성 주입 등을 사용해서). 이번 포스팅에서는 Spring Framework의 도움을 받으면 코드가 얼마나 더 간결해질 수 있는지 공부해 보겠습니다.
package org.example.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
// org.example을 root로 삼아서 이 이하에 있는 빈들을 모두 스캔하겠다
@ComponentScan("org.example")
@Configuration
public class Config {
}
위와 같이 Config 클래스를 작성하고 @Configuration, @ComponentScan 어노테이션을 붙였습니다. @ComponentScan의 인자로 패키지명을 넣었는데, 넣은 패키지 하위의 빈들을 모두 스캔하겠다는 의미입니다.
package org.example;
import org.example.config.Config;
import org.example.logic.BubbleSort;
import org.example.logic.Sort;
import org.example.service.SortService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
// Config을 불러오는 코드 추가
ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
SortService sortService = new SortService(new BubbleSort<>());
System.out.println("result " + sortService.doSort(Arrays.asList(args)));
}
}
Main 클래스도 위와 같이 수정하였습니다.
ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
작성한 Config를 불러와서 사용하도록 수정되었네요.
그리고, 이전 포스팅에서 작성했던 JavaSort, BubbleSort, SortService에도 어노테이션을 붙여 빈으로 등록하겠습니다. 모두 org.example 패키지 아래에 작성하였으니 자동으로 스캔될 것입니다.
package org.example;
import org.example.config.Config;
import org.example.logic.BubbleSort;
import org.example.logic.Sort;
import org.example.service.SortService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
SortService sortService = context.getBean(SortService.class);
System.out.println("result " + sortService.doSort(Arrays.asList(args)));
}
}
빈이 등록되었으니 Main클래스를 위처럼 수정하여, new 키워드 없이 빈에 등록된 SortService를 getBean으로 가져와서 정렬을 수행하도록 해보겠습니다. 이전에 작성했던 MainTest를 이용해서 Main 함수를 테스트 해보면
package org.example;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class MainTest {
@Test
void main() {
// given
String[] args = {"3", "1", "2"};
// when & then
Main.main(args);
}
}
No qualifying bean of type 'org.example.logic.Sort<java.lang.String>' available: expected single matching bean but found 2: bubbleSort,javaSort
위와 같은 에러가 발생하네요. 서비스 내부에 어떤 정렬 구현체를 사용해야 하는지 Spring에게 확실히 알려줄 필요가 있습니다.
package org.example.service;
import org.example.logic.JavaSort;
import org.example.logic.Sort;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class SortService {
private final Sort<String> sort;
public SortService(@Qualifier("bubbleSort") Sort<String> sort) {
this.sort = sort;
}
public List<String> doSort(List<String> list) {
return sort.sort(list);
}
}
@Qualifier("bubbleSort") 어노테이션을 붙여, SortService 생성자에 bubbleSort 빈을 주입하였습니다. 서비스 코드를 이렇게 수정하고 다시 테스트 해보면, 정상적으로 테스트가 통과됨을 확인할 수 있습니다.