org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'springConfig': Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'xik.ShoppingMall.Repository.MemberRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}

위와 같은 에러가 발생하였습니다.
위의 에러를 해석해보면 빈을 등록할 때 에러가 발생한것으로 보입니다.

 

 

[ 기존 코드 ]

//    @Autowired
//    MemberServiceInterface memberService;
//
//    @Autowired
//    OrderService orderService;
//
//    @Autowired
//    DiscountPolicy discountPolicy;

 

 

[ 새로 작성한 코드 ]

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
MemberServiceInterface memberService = applicationContext.getBean("memberService", MemberServiceInterface.class);
OrderService orderService = applicationContext.getBean("orderService", OrderService.class);

 

 

클라이언트 코드에서 스프링 컨테이너를 사용하는 버젼으로 바꾸기 위해 사용하다가 에러가 발생했습니다.

 

뭐가 문제인지 에러 코드를 다시 읽어보겠습니다.

 

 

 

UnsatisfiedDependencyException : Error creating bean with name 'springConfig'

: springConfig 라는 이름을 찾고있네요 ??

근데 제 코드를 보면 applicationContext 객체를 불러올때도 SpringConfig를 사용해주고 있고, 

애초에 클래스명도 SpringConfig로 적어둔 상태입니다.

근데 확인을 해보니 애초에 맨 앞 철자가 소문자로 변경된 형태로 등록되기 때문에 상관없는 문제라고 하네요.

 

그러면 다음 에러코드를 읽어보겠습니다.

 

: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'xik.ShoppingMall.Repository.MemberRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}

위의  코드를 읽어보니 Repository에서의 bean을 등록해줄 때 생기는 문제 같습니다.

코드의 구성도를 보면

 

[ SpringConfig ]

    public SpringConfig(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }

 

[ SpringDataJpaRepository ]

public interface SpringDataJpaMemberRepository extends JpaRepository<Member, Long>, MemberRepository {

    @Override
    Optional<Member> findByName(String name);

}

 

의심가는 부분이 있네요

applicationContext 로 getBean으로 빈들을 가져오는데,

제 코드 같은 경우 SpringConfig 생성자에서 JpaRepository로 구현된 레포지토리를 불러오는데

이 부분도  "@Autowired 로 주입을 해줄게 아니라 applicationContext로 주입을 해줘야하는게 아닌가? " 라는 생각입니다.

 

그런데 이 의문에도 확신이 없습니다.

의존성 주입과 빈 등록에 대해 확실히 알고 나서 진행해야할 것 같네요

https://tjdwns4537.tistory.com/52

 

의존성 주입과 빈 등록

IoC 컨테이너에 빈으로 등록이 되어야 의존성 주입을 할 수 있습니다. 먼저, 여러가지 의존성 주입 방법이 있지만 @Autowired를 통해서 주입하는 것을 알아보겠습니다. @Autowired 필요한 의존 객체의

tjdwns4537.tistory.com

 

정리를 해봤으니 에러에 대해 다시 분석해보겠습니다.

applicationContext는 스프링 컨테이너이고,

@Autowired는 의존관계 주입을 해주는 것이네요.

 

그러면 스프링 컨테이너에서 빈이 등록될 때 문제가 발생되는 것으로 보입니다.

 

[ IoC컨테이너 ]

[ 테스트 코드 ]

 

구성도를 보면

1) ApplicationContext 로 SpringConfig 컨테이너에 대한 빈 팩토리 생성

2) 팩토리의 getBean메서드를 통해 memberService,orderService라는 빈을 가져옴

* 그런데 memberRepository에 대한 빈이 없다는 에러내용

 => 결국, JpaRepository에서 자동으로 빈을 만들어주는 것으로 믿고 있었지만 실제로는 안만들어지고 있는것으로 보임

 => 순수 자바 코드인 MemoryMeberRepository로 @Bean등록해주고 테스트하니 정상 작동

 

 

 

* 결론

: @Autowired할 스프링 빈 대상이 없어 발생하는 문제입니다.

원인은 JpaRepository에서 빈을 등록해주는 과정에서 문제가 있는 것으로 보입니다.

그런데 JpaRepository를 상속받으면 자동으로 빈을 등록해주는 것으로 배웠는데 정확한 이유는 아직 파악하지 못했습니다.

이유를 알게되는대로 다시 블로그에 업데이트하도록 하겠습니다.

 

 

 

 

 

[ 2022.07.12 확인된 해결방법1 ]
Jpa이 버젼 문제였던 것으로 확인되었습니다.
spring-data-jpa 2버전은 spring-core 5부터 호환이 된다고 합니다.
버젼을 낮춰서 실행해보시면 해결됩니다.


[ 2022.07.12 확인된 해결방법2 ]
멤버 이름에 대소문자 구분 처리에 대한 에러가 있을 수 있다.

* 참고 : 아직 저는 해결되지 않았습니다.. ( 2022.07.19 해결됬습니다.. )

 

 

 

 

[ 해결완료 ]

해결방법 :

1) @Repository 어노테이션을 붙치면 레포지토리에 대한 에러가 @Component랑은 다르게 나온다는걸 알 게 되었습니다.

2) JpaRepository 상속받는 부분에 @Repository를 붙쳐주니 아래와 같은 에러로그를 볼 수 있습니다.

Repository인터페이스의 findall 메서드를 가리키고 있는거 같습니다. 그러면 서비스에서 findall을 호출해주는 부분을 찾아보겠습니다.

레포지토리의 findall을 실제 호출해주는 부분이 있습니다. 

 

그런데 기본적으로 jpaRepository를 상속받게 되면 기본적으로 제공되는 다양한 메서드들이 있습니다.

그 중에 findAll() 이라는 메서드가 있습니다.

그런데 저의 소스코드에서는 findAll과 같은 역할을 하는 findall 메서드가 존재합니다.

즉, 이유는 간단했습니다.

 

1) 컨트롤러에서 서비스의 findMeber() 메서드를 호출

2) findMember메서드에서는 findall() 메서드를 호출

3) 하지만 findall() 메서드는 jpaRepository 메서드와는 엄밀히 다른 메서드이므로 jpaRepository 상속 메서드 안에서 사용하고 싶다면

  따로 선언했어야함

   * JpaRepository 명명 규칙 : https://www.devkuma.com/docs/spring/jparepository%EC%9D%98-%EB%A9%94%EC%86%8C%EB%93%9C-%EB%AA%85%EB%AA%85-%EA%B7%9C%EC%B9%99/

 

4) MemberRepository에 선언되어 있는 findall() 메서드의 이름을 findAll() 로 바꿔준 후 서비스에서 사용

5) 테스트 결과 이상 없음

 

 

몇일 동안 이 문제로 삽질했는데 드디어 해결됬습니다 !!

+ Recent posts