스택큐힙리스트

왜 내 Spring @Autowired 필드가 null인가요? 본문

카테고리 없음

왜 내 Spring @Autowired 필드가 null인가요?

스택큐힙리스트 2023. 9. 3. 19:07
반응형

참고: 이것은 공통적인 문제에 대한 권위 있는 답변을 제공하기 위한 것입니다.

나는 Spring '@Service' 클래스 ( 'MileageFeeCalculator' )를 가지고 있습니다. 이 클래스는 field ( #$^@^#$@ 필드 ( # ), but the field )를 가지고 있지만, 이 필드를 사용하려고 할 때 문제가 발생합니다. 로그를 확인해 보면 'MileageFeeCalculator' 빈과 'MileageRateService' 빈 모두가 생성되었다는 것을 알 수 있지만, 서비스 빈에서 'mileageCharge' 메소드를 호출하려고 할 때 에러가 발생합니다. 왜 Spring이 필드를 자동으로 연결하지 않는 걸까요?

컨트롤러 클래스:

'@Controller

public class MileageFeeController {

@RequestMapping(/mileage/{miles})

@ResponseBody

public float mileageFee(@PathVariable int miles) {

MileageFeeCalculator calc = new MileageFeeCalculator();

return calc.mileageCharge(miles);

}

}

'

서비스 클래스:

'@Service

public class MileageFeeCalculator {

@Autowired

private MileageRateService rateService; // <--- should be autowired, is null

public float mileageCharge(final int miles) {

return (miles * rateService.ratePerMile()); // <--- throws NPE

}

}

'

서비스 빈은 'MileageFeeCalculator' 에서 자동 주입되어야 하지만 그렇지 않은 경우입니다.

'@Service

public class MileageRateService {

public float ratePerMile() {

return 0.565f;

}

}

'

'GET /mileage/3' 를 시도할 때, 다음의 예외가 발생합니다:

'java.lang.NullPointerException: null

at com.chrylis.example.spring_autowired_npe.MileageFeeCalculator.mileageCharge(MileageFeeCalculator.java:13)

at com.chrylis.example.spring_autowired_npe.MileageFeeController.mileageFee(MileageFeeController.java:14)

...

'

답변 1

'@Autowired' 가 주석 처리된 필드는 'null' 이라고 합니다. 왜냐하면 Spring이 'MileageFeeCalculator' 로 만든 복사본을 알지 못해서 자동 와이어링을 할 수 없기 때문입니다.

'The Spring Inversion of Control (IoC) container'은 세 가지 주요한 논리적 구성 요소를 가지고 있습니다: 애플리케이션에서 사용할 수 있는 구성 요소 (빈)들을 포함하는 레지스트리 ( 'ApplicationContext' ), 객체의 종속성을 빈들과 일치시켜 주입하는 구성 시스템, 그리고 많은 다른 빈들의 구성을 살펴보고 필요한 순서로 인스턴스화하고 구성하는 방법을 결정할 수 있는 종속성 해결자입니다.

IoC 컨테이너는 마법이 아니며, 자바 객체에 대해 어떠한 방법도 알지 못합니다. 어떻게 알려주지 않은 이상에선. 'new'를 호출할 때, JVM은 새로운 객체의 복사본을 인스턴스화하고 그것을 직접 전달합니다. 이 과정을 거치지 않습니다. 빈을 구성하는 세 가지 방법이 있습니다.

나는 모든 코드를 게시했고, Spring Boot를 사용하여 시작했습니다. 'this GitHub project' 에서 전체 실행 중인 프로젝트를 확인할 수 있습니다. 각 접근 방식에 필요한 모든 것을 보려면 태그를 사용하세요. 'NullPointerException' : 'nonworking'

Inject your beans

가장 선호되는 옵션은 Spring이 모든 빈들을 자동으로 연결하도록 허용하는 것입니다. 이는 코드의 양이 가장 적고 가장 유지보수하기 쉽습니다. 원하는대로 자동 연결이 작동하도록 하려면 'MileageFeeCalculator' 도 이렇게 자동 연결하십시오.

'@Controller

public class MileageFeeController {

@Autowired

private MileageFeeCalculator calc;

@RequestMapping(/mileage/{miles})

@ResponseBody

public float mileageFee(@PathVariable int miles) {

return calc.mileageCharge(miles);

}

}

'

만약 다른 요청을 위해 새로운 서비스 객체 인스턴스를 생성해야 한다면, 'the Spring bean scopes' 를 사용하여 의존성 주입을 여전히 사용할 수 있습니다.

서비스 객체를 주입하는 태그: 'working-inject-bean'

@Configurable를 사용하세요.

만약 'new' 로 생성된 객체가 autowired 되어야 한다면, 객체를 주입할 수 있습니다. 이 접근 방법은 객체의 생성자에 코드를 삽입하여 Spring에 생성되고 있다고 알리고, Spring이 새로운 인스턴스를 구성할 수 있도록 합니다. 이를 위해 빌드에 약간의 설정 (예: @!'ajc' 로 컴파일)과 Spring의 런타임 구성 핸들러 (JavaConfig 구문으로 @!'@EnableSpringConfigured' )를 활성화해야 합니다. 이 접근 방법은 Roo Active Record 시스템에서 사용되며, 엔티티의 'new' 인스턴스가 필요한 지속성 정보를 주입 받을 수 있도록 합니다.

'@Service

@Configurable

public class MileageFeeCalculator {

@Autowired

private MileageRateService rateService;

public float mileageCharge(final int miles) {

return (miles * rateService.ratePerMile());

}

}

'

서비스 객체에 '@Configurable'를 사용하는 태그입니다: 'working-configurable'

수동으로 빈을 찾는 것은 권장되지 않습니다.

이 접근 방식은 특별한 상황에서만 레거시 코드와의 인터페이스에 적합합니다. 대부분의 경우 Spring이 자동으로 연결할 수 있는 싱글톤 어댑터 클래스를 생성하는 것이 선호됩니다. 레거시 코드가 호출할 수 있지만, 스프링 응용 프로그램 컨텍스트에 직접적으로 빈을 요청할 수도 있습니다.

이를 수행하기 위해, Spring이 'ApplicationContext' 객체에 대한 참조를 제공할 수 있는 클래스가 필요합니다.

'@Component

public class ApplicationContextHolder implements ApplicationContextAware {

private static ApplicationContext context;

@Override

public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {

context = applicationContext;

}

public static ApplicationContext getContext() {

return context;

}

}

'

그럼 이제 당신의 유산 코드는 'getContext()'를 호출하고 필요한 빈(beans)을 가져올 수 있습니다:

'@Controller

public class MileageFeeController {

@RequestMapping(/mileage/{miles})

@ResponseBody

public float mileageFee(@PathVariable int miles) {

MileageFeeCalculator calc = ApplicationContextHolder.getContext().getBean(MileageFeeCalculator.class);

return calc.mileageCharge(miles);

}

}

'

태그는 Spring 컨텍스트에서 서비스 객체를 수동으로 찾아 작동합니다: 'working-manual-lookup'

답변 2

스프링 @Autowired 필드가 왜 null인지 궁금하신가요? 이 문제에 대한 원인은 다양할 수 있지만, 주로 아래와 같은 이유로 인해 발생할 수 있습니다.

1. 컴포넌트 스캔이 되지 않은 경우: 스프링은 @Autowired 어노테이션을 통해 의존성 주입을 자동으로 처리합니다. 그러나 만약 해당 필드를 스프링이 스캔하는 컴포넌트로 인식하지 못하면, 필드는 null 값으로 유지될 것입니다. 이는 주로 컴포넌트 스캔이 설정되어 있지 않은 경우에 발생할 수 있습니다.

2. 해당 필드의 빈이 존재하지 않는 경우: @Autowired 어노테이션이 적용된 필드는 스프링 컨텍스트 내에서 빈으로 등록되어야 합니다. 만약 해당 필드를 빈으로 등록하지 않았거나, 등록된 빈이 존재하지 않는 경우, 필드는 null로 유지될 것입니다. 이런 상황은 주로 빈의 등록에 오류가 있는 경우에 발생할 수 있습니다.

3. 의존성 주입 방식이 잘못된 경우: 스프링은 다양한 방식으로 의존성을 주입할 수 있습니다. @Autowired 어노테이션 자체에도 다양한 옵션을 설정할 수 있습니다. 따라서, 의존성 주입 방식 또는 설정에 오류가 있는 경우 필드는 null 값으로 유지될 수 있습니다.

위와 같은 이유로 인해 @Autowired 필드가 null로 나타날 수 있습니다. 이를 해결하기 위해서는 다음과 같은 단계들을 확인해보세요.

1. 컴포넌트 스캔이 제대로 설정되었는지 확인해보세요. 필요한 패키지가 스캔 대상에 포함되어 있는지, 컴포넌트 스캔을 활성화했는지를 확인해야 합니다.

2. 해당 필드를 스프링 컨텍스트에 빈으로 등록했는지 확인해보세요. @Component 어노테이션 또는 해당 필드를 빈으로 등록하는 설정을 추가해야 합니다.

3. 의존성 주입 방식이 올바른지 확인해보세요. @Autowired 어노테이션의 옵션을 확인하고, 필요에 따라 수정해야 합니다.

이러한 단계들을 통해 @Autowired 필드가 null로 나타나는 문제를 해결할 수 있습니다. 빈을 올바르게 등록하고, 의존성 주입 방식에 오류가 없도록 주의해야 합니다.

반응형
Comments