카테고리 없음

Spring @Autowired 필드가 null 인 이유는 무엇입니까?

기록만이살길 2021. 2. 25. 02:32
반응형

Spring @Autowired 필드가 null 인 이유는 무엇입니까?

1. 질문(문제점):

참고 : 이것은 일반적인 문제에 대한 표준 답변입니다.

필드 ( ) 가있는 Spring @Service클래스 ( MileageFeeCalculator)가 있지만 필드 를 사용하려고 할 때입니다. 로그는 빈과 빈이 모두 생성되고 있음을 보여 주지만 서비스 빈 에서 메서드 를 호출하려고 할 때마다를 얻습니다 . Spring이 필드를 자동 배선하지 않는 이유는 무엇입니까?@AutowiredrateServicenullMileageFeeCalculatorMileageRateServiceNullPointerExceptionmileageCharge

컨트롤러 클래스 :

@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)
    ...

2. 해결방안:

주석 @Autowired달린 필드 nullSpring이 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를 자동 설정할 수하기를, 당신은 할 수 있습니다 봄의 사용 @ConfigurableAspectJ를 컴파일 시간 짜기와 함께 주석을 개체를 삽입 할 수 있습니다. 이 접근 방식은 Spring이 새 인스턴스를 구성 할 수 있도록 생성 중임을 Spring에 알리는 코드를 객체의 생성자에 삽입합니다. 이를 위해서는 빌드에서 약간의 구성 (예 :으로 컴파일 ajc)하고 Spring의 런타임 구성 핸들러 ( @EnableSpringConfiguredJavaConfig 구문 사용)를 켜야합니다. 이 접근 방식은 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

65814804
반응형