참고 : 이것은 일반적인 문제에 대한 정식 답변입니다.
필드 ( ) 가있는 Spring @Service
클래스 ( MileageFeeCalculator
)가 있지만 필드는 사용하려고합니다. 로그는 Bean과 Bean이 모두 생성되고 있음을 보여 주지만 서비스 Bean 에서 메소드 를 호출하려고 할 때마다 메시지가 표시 됩니다. 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; // <--- should be autowired, is null
public float mileageCharge(final int miles) {
return (miles * rateService.ratePerMile()); // <--- throws NPE
}
}
자동 연결되어야 MileageFeeCalculator
하지만 그렇지 않은 서비스 Bean :
@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
생성 한 사본에 대해 Spring이 알지 new
못하고 자동 와이어 링을 알지 못했기 때문입니다.
IoC (Spring Inversion of Control) 컨테이너 에는 3 가지 주요 논리 구성 요소 ApplicationContext
가 있습니다. 컨텍스트에서 Bean과의 종속성 및 여러 다른 Bean의 구성을보고 필요한 순서로 인스턴스화하고 구성하는 방법을 결정할 수있는 종속성 솔버.
IoC 컨테이너는 마법이 아니며 Java 객체에 대해 알지 못하는 한 Java 객체를 알 수있는 방법이 없습니다. 를 호출 new
하면 JVM이 새 객체의 사본을 인스턴스화하여 바로 전달합니다. 구성 프로세스를 거치지 않습니다. Bean을 구성 할 수있는 세 가지 방법이 있습니다.
이 GitHub 프로젝트 에서 Spring Boot를 사용하여이 코드를 모두 게시 했습니다 . 각 접근 방식에 대해 전체 실행 프로젝트를 살펴보고 작동하는 데 필요한 모든 것을 볼 수 있습니다. 태그 NullPointerException
:nonworking
콩을 주입
가장 바람직한 옵션은 Spring이 모든 빈을 자동으로 연결하도록하는 것입니다. 이것은 가장 적은 양의 코드를 요구하며 가장 유지 보수가 쉽습니다. 원하는대로 자동 배선을 수행하려면 다음 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
와이어 링으로 생성 된 객체가 실제로 필요한 경우 AspectJ 컴파일 타임 직조와 함께 Spring @Configurable
주석을 사용 하여 객체를 주입 할 수 있습니다. 이 접근법은 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
수동 Bean 조회 : 권장하지 않음
이 방법은 특별한 상황에서 레거시 코드와의 인터페이스에만 적합합니다. Spring이 자동 와이어 링하고 레거시 코드가 호출 할 수있는 싱글 톤 어댑터 클래스를 작성하는 것이 거의 항상 바람직하지만 Spring 애플리케이션 컨텍스트에 Bean을 직접 요청할 수 있습니다.
이를 위해서는 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()
필요한 Bean을 호출 하고 검색 할 수 있습니다 .
@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
답변
웹 애플리케이션을 코딩하지 않는 경우 @Autowiring이 수행되는 클래스가 스프링 빈인지 확인하십시오. 일반적으로 스프링 컨테이너는 스프링 빈으로 생각할 수있는 클래스를 인식하지 못합니다. 스프링 클래스에 대해 스프링 컨테이너에 알려줘야합니다.
appln-contxt에서 구성하여 얻을 수 있거나 클래스에 @Component 로 주석을 달고 더 나은 방법 은 새 연산자를 사용하여 주석이 달린 클래스를 만들지 마십시오. 아래와 같이 Appln-context에서 가져와야합니다.
@Component
public class MyDemo {
@Autowired
private MyService myService;
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("test");
ApplicationContext ctx=new ClassPathXmlApplicationContext("spring.xml");
System.out.println("ctx>>"+ctx);
Customer c1=null;
MyDemo myDemo=ctx.getBean(MyDemo.class);
System.out.println(myDemo);
myDemo.callService(ctx);
}
public void callService(ApplicationContext ctx) {
// TODO Auto-generated method stub
System.out.println("---callService---");
System.out.println(myService);
myService.callMydao();
}
}
답변
실제로, JVM 관리 오브젝트 또는 스프링 관리 오브젝트를 사용하여 메소드를 호출해야합니다. 컨트롤러 클래스의 위 코드에서 자동 유선 객체가있는 서비스 클래스를 호출하는 새 객체를 만들고 있습니다.
MileageFeeCalculator calc = new MileageFeeCalculator();
그렇게 작동하지 않습니다.
솔루션은이 MileageFeeCalculator를 컨트롤러 자체의 자동 유선 객체로 만듭니다.
아래와 같이 컨트롤러 클래스를 변경하십시오.
@Controller
public class MileageFeeController {
@Autowired
MileageFeeCalculator calc;
@RequestMapping("/mileage/{miles}")
@ResponseBody
public float mileageFee(@PathVariable int miles) {
return calc.mileageCharge(miles);
}
}
답변
내가 익숙하지 않을 때 같은 문제가 발생했습니다 the life in the IoC world
. @Autowired
내 콩의 하나의 필드는 런타임에 null입니다.
근본 원인은 Spring IoC 컨테이너에 의해 유지 관리되는 자동 작성 Bean ( @Autowired
필드가 indeed
올바르게 주입 됨) 을 사용하는 대신 newing
해당 Bean 유형의 고유 한 인스턴스입니다. 물론 @Autowired
스프링은 필드를 주입 할 기회가 없기 때문에이 필드는 null입니다.
답변
당신의 문제는 새로운 것입니다 (자바 스타일의 객체 생성)
MileageFeeCalculator calc = new MileageFeeCalculator();
주석으로 @Service
, @Component
, @Configuration
콩이 만들어집니다
서버가 시작될 때 스프링의 애플리케이션 컨텍스트. 그러나 new 연산자를 사용하여 객체를 만들면 이미 만들어진 응용 프로그램 컨텍스트에 객체가 등록되지 않습니다. 예를 들어 Employee.java 클래스를 사용했습니다.
이것 좀 봐:
public class ConfiguredTenantScopedBeanProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
String name = "tenant";
System.out.println("Bean factory post processor is initialized");
beanFactory.registerScope("employee", new Employee());
Assert.state(beanFactory instanceof BeanDefinitionRegistry,
"BeanFactory was not a BeanDefinitionRegistry, so CustomScope cannot be used.");
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
for (String beanName : beanFactory.getBeanDefinitionNames()) {
BeanDefinition definition = beanFactory.getBeanDefinition(beanName);
if (name.equals(definition.getScope())) {
BeanDefinitionHolder proxyHolder = ScopedProxyUtils.createScopedProxy(new BeanDefinitionHolder(definition, beanName), registry, true);
registry.registerBeanDefinition(beanName, proxyHolder.getBeanDefinition());
}
}
}
}
답변
나는 Spring을 처음 사용하지만이 작업 솔루션을 발견했습니다. 그것이 혐오스러운 방법인지 알려주세요.
applicationContext
이 빈에 Spring을 주입합니다 .
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
@Component
public class SpringUtils {
public static ApplicationContext ctx;
/**
* Make Spring inject the application context
* and save it on a static variable,
* so that it can be accessed from any point in the application.
*/
@Autowired
private void setApplicationContext(ApplicationContext applicationContext) {
ctx = applicationContext;
}
}
원하는 경우이 코드를 기본 응용 프로그램 클래스에도 넣을 수 있습니다.
다른 클래스는 다음과 같이 사용할 수 있습니다.
MyBean myBean = (MyBean)SpringUtils.ctx.getBean(MyBean.class);
이런 식으로 응용 프로그램의 모든 객체 (와 함께 new
)와 정적 방식으로 Bean을 얻을 수 있습니다 .
답변
드문 경우이지만 여기에 나에게 일어난 일이 있습니다.
@Inject
대신 @Autowired
Spring에서 지원하는 javaee 표준을 사용 했습니다 . 모든 장소는 잘 작동했고 콩은 한곳이 아닌 올바르게 주입되었습니다. 콩 주입은 같은 것 같습니다
@Inject
Calculator myCalculator
마침내 우리는 (실제로 Eclipse 자동 완성 기능) com.opensymphony.xwork2.Inject
대신에 가져온 오류라는 것을 발견했습니다.javax.inject.Inject
!
그래서 반드시 주석 (즉, 메이크업을 요약하면 @Autowired
, @Inject
, @Service
, …) 올바른 패키지가!