-
Spring-Bean Container에 대한 예제와 이에 대한 후기소프트웨어 및 프로그래밍/Spring 2024. 10. 14. 16:18
예제에 대한 다이어그램은 아래와 같다
SampleService 인터페이스와 ServiceA 인터페이스가 존재하고 SampleService의 구현체 SampleServiceImpl와 ServiceA의 구현체 ServiceAImpTypeOne, ServiceAImplTypeTwo 존재한다.
SampleService에 대한 구동은 SampleServiceImpl에 구현되어 있고 SampleServiceImpl는 ServiceA를 멤버로 소유하고 있다.
SampleServiceImpl의 멤버인 ServiceA에 대해 구현체를 new를 통해 바로 할당해서 사용해도 되지만, 아래와 같이 SampleServiceImpl이 ServiceA의 구현체에 의존하기 보다는 인터페이스인 ServiceA에 대해서만 의존하기 위해 코드를 아래와 같이 구현하였다.
public class SampleServiceImpl implements SampleService { @Autowired private final ServiceA serviceA; // 인터페이스에만 의존하고 있음 public SampleServiceImpl(ServiceA serviceA){ this.serviceA = serviceA; } @Override public void printService() { serviceA.printService(); System.out.println(">> ServiceA created"); } }
위와 같이 코드를 구성함으로 SampleServiceImpl는 ServiceA의 구현체가 어떤 구현체인지 상관하지 않는다.
그리고 ServiceA의 구현체를 주입하기 위한 코드를 아래와 같이 분리하였다.
Spring을 사용하지 않고 의존성 주입
/* 의존성을 주입하기 위한 Config로 코드 분리 */ public class AppConfig { public ServiceA serviceA(){ // AppConfig만 수정해서 다른 인터페이스 종속성을 결정할 수 있음 // return new ServiceAImpTypeOne(); return new ServiceAImplTypeTwo(); } public SampleService sampleService(){ return new SampleServiceImpl(serviceA()); } }
실행 부분에서 종속성을 주입할 AppConfig를 생성하고 사용할 서비스를 호출할 수 있다.
public static void main(String[] args){ AppConfig appConfig = new AppConfig(); var service = appConfig.sampleService(); service.printService(); }
Spring을 사용한 의존성 주입
이 AppConfig 역할을 Spring에서는 자체적으로 제공해준다. 이를 활용해서 AppConfig를 아래와 같이 수정하였다.
@Configuration public class SpringAppConfig { @Bean() public ServiceA serviceA(){ return new ServiceAImplTypeTwo(); } @Bean public SampleService sampleService(){ return new SampleServiceImpl(serviceA()); } }
@Configuration 이란 annotaion을 통해 해당 클래스는 Spring의 의존성을 관리하는 config라는 것을 알려줌과 의존성을 관리하는 기능을 수행한다. 그리고 @Bean을 통해 클래스를 Spring-Bean으로 등록한다. 등록된 Bean은 싱글톤으로 관리된다.
(아마 Map으로 관리될 것이다. 생성자의 호출이 각각 한번씩만 호출되는 것을 보면 Map을 통해 존재하는지 파악하고 없으면 등록할 듯 << todo 이것에 대해서 확인하고 추후 수정할 것!!)
public static void main(String[] args){ ApplicationContext ac = new AnnotationConfigApplicationContext(SpringAppConfig.class); SampleService service = ac.getBean("sampleService", SampleService.class); service.printService(); }
등록된 Spring-Bean들은 getBean 메서드를 통해 꺼내올 수 있다.
그리고 spring은 이렇게 수동으로 Bean을 등록하는 방법 외에 자동으로 Bean을 등록할 수 있다.
@Component @Primary public class ServiceAImpTypeOne implements ServiceA{ @Autowired public ServiceAImpTypeOne(){ } @Override public void printService() { System.out.println(">>> ServiceAImpTypeOne created"); } } @Component public class ServiceAImplTypeTwo implements ServiceA{ @Autowired public ServiceAImplTypeTwo(){ } @Override public void printService() { System.out.println(">>> ServiceAImplTypeTwo created"); } }
@Component 라는 태그를 붙여줌으로 Bean의 등록 대상이라는 것을 명시한다. (@Primary는 하나의 인터페이스에 두 가지의 종속성이 야기 될 때, Primary가 먼저 적용된다는 것을 의미)
@Configuration @ComponentScan( excludeFilters = @ComponentScan.Filter(type= FilterType.ANNOTATION, classes = Configuration.class) ) public class SpringAutoAppConfig { }
@ComponentScan을 통해 @Component가 붙여진 Class를 자동으로 수집하여 Spring-Bean의 container에 저장하게 된다.
public class SpringServiceAutoApp { public static void main(String[] args){ ApplicationContext ac = new AnnotationConfigApplicationContext(SpringAutoAppConfig.class); //SampleService service = ac.getBean("sampleService", SampleService.class); ServiceA service = ac.getBean("serviceAImpTypeTwo", ServiceA.class); service.printService(); } }
사용은 수동 등록과 동일하게 getBean 메서드를 통해 container에 꺼내어 사용하면 된다.
후기
기존에는 인터페이스 선언에 대한 구현체를 바로 할당하여 사용했다. 그렇게 함으로 간단히 사용할 수 있었지만 코드 여기저기에 구현체 할당 부분이 남게 될 수 있다. 반면 Spring에서는 이러한 구현체 할당 부분을 한 곳으로 모아 하나의 코드 파일로 관리를 하였다. 그렇게 함으로 생성할 때 일괄로 싱글톤으로 관리하는 등 최적화를 이루어낼 수 있다. 당연히 Spring 프레임워크에 익숙해지면 이러한 관리 기법의 활용성이 뛰어날 것이다. 한편으로 Spring 프레임워크 내부에서 자동으로 처리하는 부분이 많아지므로 나중에 문제가 발생했을 시, 디버깅의 어려울 것 같다는 생각이 든다. 이러한 문제는 마치 DB의 정규화와 비슷하다. 정규화를 많이 거침으로 데이터의 정합성과 유지보수성을 증가시켰지만 DB 복잡성을 높인 것처럼 Spring의 이러한 설계도 장단점이 뚜렷하다.
'소프트웨어 및 프로그래밍 > Spring' 카테고리의 다른 글
Spring-Security (0) 2025.02.13 Spring JPA에서 연관관계 맵핑 (0) 2025.01.11 Spring(벡엔드)가 최종적으로 제공하는 것 (0) 2024.10.14 Spring SOLID 원칙 (1) 2024.09.24