프로그래밍 언어/Spring MyBatis

Spring @Autowired 필드가 null 인 이유

Rateye 2021. 6. 28. 10:13
728x90
반응형
질문 : Spring @Autowired 필드가 null 인 이유는 무엇입니까?

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

@Autowired 필드 ( rateService )가있는 Spring @Service 클래스 ( MileageFeeCalculator )가 있지만 사용하려고 할 때 필드가 null 로그에 MileageFeeCalculator 빈과 MileageRateService 빈이 모두 생성되고 있음이 표시되지만 서비스 빈 mileageCharge 메서드를 호출하려고 할 때마다 NullPointerException 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)
...
답변

@Autowired 주석이 달린 필드는 new 만든 MileageFeeCalculator의 복사본에 대해 알지 MileageFeeCalculator 하는 것을 null 입니다.

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

new 로 생성 된 객체가 자동 연결되도록 정말로 필요한 경우 AspectJ 컴파일 타임 위빙과 함께 Spring @Configurable 주석을 사용하여 객체를 주입 할 수 있습니다. 이 접근 방식은 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이 autowire 할 수 있고 레거시 코드가 호출 할 수있는 singleton 어댑터 클래스를 만드는 것이 거의 항상 바람직하지만 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

출처 : https://stackoverflow.com/questions/19896870/why-is-my-spring-autowired-field-null
728x90
반응형