티스토리 뷰




저번 포스팅(http://emflant.tistory.com/33) 에서 예제프로젝트를 하나 만들었다.

[New] - [Spring Template Project] - [Simple Spring Utility Project]

 

 

우선 설정파일이 2개가 있다.

 

app-context.xml : 메인 설정파일.

ExampleConfigurationTests-context.xml : 테스트용 설정파일.

 

테스트용 설정파일에 보면 <import resource="classpath:/META-INF/spring/app-context.xml"/>  문법이 있는데 딱봐도 app-context.xml 파일을 포함하는 문구다. JUnit 테스트용이니 실제 config 파일을 건드리지 않기위해 참조만 하는 형식으로 해놓은 거 같다.

그러면 이 ExampleConfigurationTests-context.xml 은 누가 읽어서 쓸까. 바로 ExampleConfigurationTests.java 파일에서 사용한다. 소스를 보면

@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class ExampleConfigurationTests {
	
	@Autowired
	private Service service;

	@Test
	public void testSimpleProperties() throws Exception {
		assertNotNull(service);
	}
	
}

@ContextConfiguration, @RunWith(SpringJUnit4ClassRunner.class) 어노테이션이 있다. 모하는 놈들일까. 바로 JUnit 에서의 스프링 통합테스트를 할때 사용하는 놈이다.  

 

@RunWith(SpringJUnit4ClassRunner.class) 은 스프링 컨텍스트에 설정된 Bean 객체들을 JUnit 테스트에 사용하고 싶을 때 선언해 준다. @ContextConfiguration 은 JUnit 테스트시 사용될 컨텍스트 설정 파일의 경로를 지정하는 어노테이션이며, 지정하지 않으면 같은 패키지에서 자신의 클래스 이름 뒤에 "-context.xml" 이름인 파일을 기본으로 찾는다. 이로써 spring 설정파일을 읽으려고 굳이 ClassPathXmlApplicationContext 클래스를 이용하지 않아도 된다.

 

클래스 내부에 필드에 보면 @Autowired 라고 있다. 해당 필드에 자동으로 Bean을 매핑해줄 때 쓰는데, 여기엔 주의할 점이 하나 있다. @Autowired 는 타입으로 식별하기 때문이다. Service 는 프로젝트 상의 인터페이스 이다. 현재 그 인터페이스를 구현한 클래스는 ExampleService 로 하나 있다.

@Component
public class ExampleService implements Service {
     
    /**
     * Reads next record from input
     */
    public String getMessage() {
        return "Hello world!"; 
    }
 
}

@Component 어노테이션은 스프링 설정파일에 <bean> 태그로 등록하지 않고 어노테이션으로 클래스에 마크해놓으면 자동으로 Bean 객체로 인식할 수 있는 어노테이션이다. 물론 자동으로 인식하기 위해서 app-context.xml 파일에는

 

<context:component-scan base-package="com.spring.sample" />

 

component-scan 태그가 써 있다.

 

어쨋든 본론은 @Autowired 는 타입으로 Bean객체를 매핑하므로 현재 Serivce 인터페이스를 구현한 클래스들 중에 @Component 어노테이션이 붙은 클래스는 현재 하나이기 때문에 지금은 아무런 문제가 없다는 것이다. 만약 Service 인터페이스를 구현한 클래스를 하나 더 만들고 그 클래스에다 @Component 어노테이션을 붙여서 테스트 코드를 실행하면 어떻게 될까. 추가한 클래스는 ExampleService2 로 명명했다. 테스트 코드를 실행하니 역시나 에러다.

 

ExampleConfigurationTests (2)
com.spring.sample.ExampleConfigurationTests
testSimpleProperties(com.spring.sample.ExampleConfigurationTests)
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'com.spring.sample.ExampleConfigurationTests': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.spring.sample.Service com.spring.sample.ExampleConfigurationTests.service; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [com.spring.sample.Service] is defined: expected single matching bean but found 2: [exampleService, exampleService2]

 at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:288)

 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1074)

 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireBeanProperties(AbstractAutowireCapableBeanFactory.java:374)

 at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:110)

 at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:75)

 at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:321)

 at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:211)

 at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:287)

 at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)

 at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:289)

 at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231)

 at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)

 at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)

 at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)

 at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)

 at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)

 at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)

 at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)

 at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)

 at org.junit.runners.ParentRunner.run(ParentRunner.java:236)

 at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174)

 at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)

 at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)

 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)

 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)

 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)

 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.spring.sample.Service com.spring.sample.ExampleConfigurationTests.service; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [com.spring.sample.Service] is defined: expected single matching bean but found 2: [exampleService, exampleService2]

 at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:508)

 at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:84)

 at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:285)

 ... 26 more

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [com.spring.sample.Service] is defined: expected single matching bean but found 2: [exampleService, exampleService2]

 at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:800)

 at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:707)

 at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:480)

 ... 28 more

 

우.. 요점은 No unique bean of type [com.spring.sample.Service] is defined: expected single matching bean but found 2: [exampleService, exampleService2] 이것이다. Service 타입으로 등록되어 있는 bean 은 2개라는 에러다. @Autowired 로 선언했으면 무조건 한개의 빈을 등록해야 문제가 없다는 것이다.

 

이 문제를 해결하려면 @Component 를 구현클래스 중에서 하나만 선언해야 할듯 하지만, 꼭 그렇게 안하더라도 다른 방법이 하나 있다.

바로 @Qualifier 어노테이션을 쓰면 된다.

@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class ExampleConfigurationTests {
	
	@Autowired
 	@Qualifier("exampleService2")
	private Service service;

	@Test
	public void testSimpleProperties() throws Exception {
		assertNotNull(service);
	}
	
}

이렇게 선언하면 아무런 에러 없이 잘된다. 2개의 구현클래스중 하나를 선언해주면 필터링하듯이 선언한 Bean 을 매핑해준다.

 

사실 이걸 보는 이유는.. 스프링 문법은 너무나 다양하게 존재한다. 책을 보면 이렇게도 가능하고 저렇게도 가능하고 수많은 방법이 있으니 알아서 써라 인데.. 사실 갈피잡기가 쉽지 않았다. 예제로 만들어지는 소스이니 대강 스프링측(?)에서 추천하는 방식들이 아닐까해서 좀 뜯어봤다. xml 로 bean을 일일이 등록하기 보다는 @Component 사용하고, JUnit 테스트시에는 이런식으로 하며, maven 구조에서의 spring 구현 설정 등등..  spring은 아직 내게 많이 어렵다.






반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함