참고 : 이것은 일반적인 문제에 대한 표준 답변입니다.
필드 ( ) 가있는 Spring @Service
클래스 ( MileageFeeCalculator
)가 있지만 필드 를 사용하려고 할 때입니다. 로그는 빈과 빈이 모두 생성되고 있음을 보여 주지만 서비스 빈 에서 메서드 를 호출하려고 할 때마다를 얻습니다 . Spring이 필드를 자동 배선하지 않는 이유는 무엇입니까?@Autowired
rateService
null
MileageFeeCalculator
MileageRateService
NullPointerException
mileageCharge
컨트롤러 클래스 :
@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;
public float mileageCharge(final int miles) {
return (miles * rateService.ratePerMile());
}
}
자동 연결되어야 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)
...
주석 @Autowired
이 달린 필드 는 null
Spring이 MileageFeeCalculator
당신이 만든 복사본에 대해 new
알지 못하고 자동 연결하는 것을 몰랐기 때문입니다.
Spring Inversion of Control (IoC) 컨테이너 에는 세 가지 주요 논리적 구성 요소 ApplicationContext
가 있습니다. 애플리케이션에서 사용할 수있는 구성 요소 (빈) 의 레지스트리 (라고 함 ), 개체의 종속성을 일치시켜 개체의 종속성을 주입하는 구성자 시스템 컨텍스트에서 Bean과의 종속성 및 여러 다른 Bean의 구성을보고 필요한 순서로 인스턴스화 및 구성하는 방법을 결정할 수있는 종속성 솔버.
IoC 컨테이너는 마술이 아니며, 어떻게 든 알려주지 않는 한 Java 개체에 대해 알 수있는 방법이 없습니다. 를 호출 new
하면 JVM이 새 개체의 복사본을 인스턴스화하여 사용자에게 직접 전달합니다. 구성 프로세스를 거치지 않습니다. Bean을 구성 할 수있는 세 가지 방법이 있습니다.
이 GitHub 프로젝트 에서 Spring Boot를 사용하여이 코드를 모두 게시 했습니다 . 각 접근 방식에 대해 전체 실행중인 프로젝트를 살펴보고 작동하는 데 필요한 모든 것을 확인할 수 있습니다. 태그 NullPointerException
:nonworking
당신의 콩을 주입
가장 바람직한 옵션은 Spring이 모든 bean을 자동 연결하도록하는 것입니다. 이것은 최소한의 코드를 필요로하며 가장 관리하기 쉽습니다. 자동 연결이 원하는대로 작동하도록하려면 다음 MileageFeeCalculator
과 같이 자동 연결합니다.
@Controller
public class MileageFeeController {
@Autowired
private MileageFeeCalculator calc;
@RequestMapping("/mileage/{miles}")
@ResponseBody
public float mileageFee(@PathVariable int miles) {
return calc.mileageCharge(miles);
}
}
다른 요청에 대해 서비스 객체의 새 인스턴스를 만들어야하는 경우에도 Spring Bean 범위를 사용하여 주입을 사용할 수 있습니다 .
@MileageFeeCalculator
서비스 객체 를 삽입하여 작동하는 태그 :working-inject-bean
@Configurable 사용
당신이 정말로으로 만든 개체가 필요한 경우 new
를 자동 설정할 수하기를, 당신은 할 수 있습니다 봄의 사용 @Configurable
AspectJ를 컴파일 시간 짜기와 함께 주석을 개체를 삽입 할 수 있습니다. 이 접근 방식은 Spring이 새 인스턴스를 구성 할 수 있도록 생성 중임을 Spring에 알리는 코드를 객체의 생성자에 삽입합니다. 이를 위해서는 빌드에서 약간의 구성 (예 :으로 컴파일 ajc
)하고 Spring의 런타임 구성 핸들러 ( @EnableSpringConfigured
JavaConfig 구문 사용)를 켜야합니다. 이 접근 방식은 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이 autowire하고 레거시 코드가 호출 할 수있는 싱글 톤 어댑터 클래스를 만드는 것이 거의 항상 바람직하지만 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()
필요한 빈을 호출 하고 검색 할 수 있습니다 .
@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