목차
[스프링] 자동으로 빈 와이어링하기(Bean Auto Wiring)
빈 와이어링 설정하는 방법은 2가지가 있다.
1. Component Scanning : Spring Application Context에서 생성되는 빈을 자동으로 설정
2. Auto Wiring : 스프링이 자동으로 빈 의존성을 충족시키는 것.
1. Component Scanning
(1) XML을 이용한 방법
(2) 클래스에서 @ComponentScan을 이용한 방법
- <context:component-scan base-package=“패키지명”/> 을 이용하면된다.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd">
<context:component-scan base-package="com.spring.study"/>
</beans>
(2) 클래스에서 @ComponentScan을 이용한 방법
- @ComponentScan(basePackages=“패키지명”) 을 사용하면된다.
@Configuration
//@ComponentScan("com.spring.study")
@ComponentScan(basePackages="com.spring.study")
public class CDPlayerConfig {
}
2. Auto Wiring
@Autowired 는 생성자, 메서드, 필드에서 사용할 수 있는 annotation 이다.
Ex)
- 인터페이스 정의
public interface CDPlayerDisc {
public void play();
}
public interface MediaPlayer {
public void play();
}
- 구현 클래스 정의
@Component
public class SgtPlayer implements CDPlayerDisc {
@Override
public void play() {
System.out.println("Play Music Start~~");
}
}
@Component
public class CDPlayer implements MediaPlayer {
private CDPlayerDisc disc;
@Autowired // CDPlayerDisc 빈이 없으면 시작 단계에서 실패
public CDPlayer(CDPlayerDisc disc) {
this.disc = disc;
}
@Override
public void play() {
disc.play();
}
}
- 결과 테스트
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = CDPlayerConfig.class) //2번
//@ContextConfiguration(locations= "classpath:context.xml") //1번
public class TestPlay {
@Autowired
private CDPlayerDisc cdPlayerDisc;
@Autowired
private MediaPlayer mediaPlayer;
@Test
public void cdShouldNotBeNull() {
assertNotNull(cdPlayerDisc);
}
@Test
public void testMedia() {
mediaPlayer.play();
}
}
스캔과 주입은 서로 다른 단계
@ComponentScan은 지정한 패키지에서 빈 후보를 찾는 단계다. @Autowired나 단일 생성자의 주입은 이미 등록된 빈 중에서 필요한 타입을 고르는 단계다. CDPlayer를 만들 때 SgtPlayer가 스캔 범위 밖이라면 CDPlayer의 생성자에 아무리 @Autowired를 붙여도 구현을 찾지 못한다.
flowchart LR A["컴포넌트 스캔"] --> B["SgtPlayer 빈 정의"] A --> C["CDPlayer 빈 정의"] B --> D["CDPlayerDisc 후보 검색"] D --> E["CDPlayer 생성자 주입"] C --> E
원래 예제의 @Autowired(required=false)는 오해하기 쉽다. 유일한 생성자의 인수가 없다면 CDPlayer를 정상적으로 만들 수 없고, 이후 disc.play()도 불가능하다. 필수 협력자는 생성자에서 필수로 받는다. 정말 선택적이라면 Optional<CDPlayerDisc> 또는 아무 동작도 하지 않는 명시적 기본 구현처럼 없을 때의 동작을 코드로 정한다.
@Component
public class CDPlayer implements MediaPlayer {
private final CDPlayerDisc disc;
public CDPlayer(CDPlayerDisc disc) {
this.disc = disc;
}
@Override
public void play() {
disc.play();
}
}
생성자가 하나뿐이면 최신 Spring에서 @Autowired를 생략할 수 있다. XML 스캔 설정과 Java @ComponentScan을 모두 등록해도 의미가 같은 것은 아니다. 둘 중 어떤 ApplicationContext가 설정을 읽는지 확인한다. 특히 웹 애플리케이션에서 부모·자식 컨텍스트가 나뉘면 빈이 한쪽에만 있어 주입이 실패할 수 있다.
시작 시 NoSuchBeanDefinitionException이라면 검색 범위와 어노테이션을, 후보가 둘 이상이라면 타입과 한정자를, 생성 중 순환 오류라면 의존 방향을 본다. 빈이 정상 생성됐는데 play()가 호출되지 않으면 그때는 요청 경로와 호출 객체를 조사한다. “스캔 실패”와 “호출 실패”는 서로 다른 단계다.
참고: 컴포넌트 스캔, @Autowired 생성자 규칙.