programing

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

prostudy 2022. 8. 8. 15:16
반응형

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

주의: 이것은 일반적인 문제에 대한 정식 답변입니다.

이 있다.@Service클래스)MileageFeeCalculator )이 붙어 있습니다.@Autowiredfield)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하지만 그렇지 않다.

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

필드에는 ""라는 주석이 붙어 있습니다.@Autowirednull 봄은 그 때문이다.MileageFeeCalculator 그런 노래입니다.new★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

Spring Inversion of Control(IoC) 컨테이너에는 3개의 주요 논리 컴포넌트(레지스트리라고 함)가 있습니다.ApplicationContext응용 프로그램에서 사용할 수 있는 컴포넌트(빈), 콘텍스트 내의 빈과 의존관계를 일치시킴으로써 오브젝트의 의존관계를 삽입하는 컨피규레이션시스템 및 많은 다른 빈의 구성을 보고 필요한 경우 또는 필요한 경우 인스턴스화 및 설정방법을 결정할 수 있는 의존관계솔러데르

IoC io io io io io io io 。또한 Java 오브젝트에 대해 알 수 있는 방법이 없습니다.했을 때newJVM은 새 개체의 복사본을 인스턴스화하여 사용자에게 직접 전달합니다. 구성 프로세스를 거치지 않습니다.콩을 설정하는 방법에는 세 가지가 있습니다.

저는 이 GitHub 프로젝트에서 Spring Boot을 사용하여 이 모든 코드를 올렸습니다.각 접근법에 대해 실행 중인 전체 프로젝트를 보고 작동에 필요한 모든 것을 확인할 수 있습니다.태그 지정:

콩을 주입하다

가장 바람직한 옵션은 스프링이 모든 콩을 자동으로 배선하도록 하는 것입니다. 이는 최소한의 코드만 필요로 하며 가장 유지보수가 용이합니다.을 원하는시키려면 자동 을 해야 .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서비스 오브젝트:

@Configurable 사용

를 사용하여 new스프링 주석을 AspectJ 컴파일 시간 위빙과 함께 사용하여 객체를 주입할 수 있습니다.이 접근법은 Spring이 새 인스턴스를 구성할 수 있도록 Spring에게 경고하는 코드를 객체의 생성자에 삽입합니다.이를 위해서는 빌드에 약간의 구성이 필요합니다(예를 들어 컴파일 등).ajc 핸들러스프링)를 @EnableSpringConfiguredJava Config " " " " " " " " 。 시스템에서 되며, " Active Record"를 허용합니다.new필요한 지속성 정보를 얻기 위한 엔티티의 인스턴스입니다.

@Service
@Configurable
public class MileageFeeCalculator {

    @Autowired
    private MileageRateService rateService;

    public float mileageCharge(final int miles) {
        return (miles * rateService.ratePerMile());
    }
}

@Configurable서비스 오브젝트:

수동 콩 조회: 권장 안 함

이 방법은 특수한 상황에서 레거시 코드와의 인터페이스에만 적합합니다.스프링이 자동 배선할 수 있고 레거시 코드가 호출할 수 있는 싱글톤어댑터 클래스를 작성하는 것은 거의 항상 권장되지만 스프링 애플리케이션 컨텍스트에 직접 빈을 요청할 수 있습니다.

이 봄을 할 수 합니다.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 컨텍스트에서 서비스 개체를 수동으로 검색하여 동작하는 태그:

웹 어플리케이션을 코딩하지 않을 경우 @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);
    }
}

IOC 세계에서의 생활에 익숙하지 않을 때 같은 문제를 겪은 적이 있습니다.@Autowired늘이다

은 Spring Spring IoC 컨테이너)에 Spring IoC를 사용하는 것입니다@Autowired가 올바르게 주입되었습니다.) 는 제로로 field) )) )) )) )) )) ) 。new사람은요즘@Autowired스프링이 필드를 주입할 기회가 없기 때문에 필드가 null입니다.

새로운 문제(Java 스타일로 객체 생성)

MileageFeeCalculator calc = new MileageFeeCalculator();

포함 ★★★★★@Service,@Component,@Configuration
봄.그러나 새 연산자를 사용하여 개체를 만들면 개체가 이미 생성된 애플리케이션 컨텍스트에 등록되지 않습니다.사용한 클래스입니다.

이것 좀 봐.

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());
        }
    }
}

}

봄은 처음이지만, 이 방법을 발견했어요.그것이 바람직하지 않은 방법이라면 가르쳐 주세요.

를 놓다.applicationContext빈의 : 이콩 :

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)와 정적인 방법을 사용합니다.

드문 경우처럼 보이지만 제게 일어난 일은 다음과 같습니다.

는 리 we we we we we를 사용했다.@Inject@Autowired자바이어느 곳이나 잘 작동했고 콩은 한 군데가 아니라 정확하게 주입되었다.

@Inject
Calculator myCalculator

실제로는 Eclipse auto complete 이 Import한 을 알 수 .com.opensymphony.xwork2.Injectjavax.inject.Inject!

요약하자면, 주석)을 해 주세요.@Autowired,@Inject,@Service )는 올바른 패키지를 있습니다!, ... ).

여기서 언급되지 않은 것은 이 기사에서 "집행 순서" 단락에 설명되어 있습니다.

클래스에서 @Component 또는 파생상품 @Service 또는 @Repository(더 많은 것 같습니다)를 사용하여 주석을 달아 다른 컴포넌트를 자동 배선해야 한다는 것을 "학습"한 후, 이러한 다른 컴포넌트는 부모 컴포넌트의 컨스트럭터 내에서 여전히 늘이라는 생각이 들었습니다.

@PostConstruct를 사용하면 다음과 같은 문제를 해결할 수 있습니다.

@SpringBootApplication
public class Application {
    @Autowired MyComponent comp;
}

또, 다음과 같이 합니다.

@Component
public class MyComponent {
    @Autowired ComponentDAO dao;

    public MyComponent() {
        // dao is null here
    }

    @PostConstruct
    public void init() {
        // dao is initialized here
    }
}

다음 중 하나가 작동합니다.

  1. @Autowired 를 사용하고 있는 클래스는 Bean 이 아닙니다(new() 를 사용하고 있을 가능성이 있습니다).

  2. SpringConfig 클래스 내에서는 스프링이 @Component를 찾아야 하는 패키지에 대해 언급하지 않았습니다(@ComponentScan(basePackages "여기")).

위의 2개가 동작하지 않으면 System.out.println()을 삽입하여 어디가 잘못되었는지 확인합니다.

테스트 수업 중에 이런 일이 발생할 경우 수업에 주석을 다는 것을 잊지 마십시오.

예를 들어, Spring Boot의 경우:

@RunWith(SpringRunner.class)
@SpringBootTest
public class MyTests {
    ....

시간이 좀 지나면...

스프링 부츠는 계속 진화하고 있습니다.더 이상 사용할 필요가 없습니다.@RunWith 올바른 버전의 JUnit을 사용하는 경우.

★★★의 @SpringBootTest '혼자서 일하세요'를 .@TestJUnit4가 아닌 JUnit5에서.

//import org.junit.Test; // JUnit4
import org.junit.jupiter.api.Test; // JUnit5

@SpringBootTest
public class MyTests {
    ....

컴파일되지만, " " " 는 " " 입니다.@Autowired ★★★★★★★★★★★★★★★★★」@Value를 들어는 "(")"가 .null스프링 부트는 마법에 의해 동작하기 때문에 이 장애를 직접 디버깅할 수 있는 방법은 거의 없습니다.

다른 은 전화를 거는 SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this)
MilageFeeCalculator:

@Service
public class MileageFeeCalculator {

    @Autowired
    private MileageRateService rateService; // <--- will be autowired when constructor is called

    public MileageFeeCalculator() {
        SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this)
    }

    public float mileageCharge(final int miles) {
        return (miles * rateService.ratePerMile()); 
    }
}

스프링에 주석을 단 수업을 스캔하도록 지시하는 것을 놓치신 것 같습니다.

하시면 됩니다.@ComponentScan("packageToScan")스프링 애플리케이션의 구성 클래스에서 스프링 스캔을 지시합니다.

@Service, @Component기타 주석이 메타 설명을 추가합니다.

스프링은 빈으로 생성되거나 주석으로 표시된 클래스의 인스턴스만 주입합니다.

한 후.@ComponentScan주석으로 표시된 클래스에 대한 스프링 룩을 지시합니다.이 때@Autowired관련 빈을 검색하여 필요한 인스턴스를 주입합니다.

주석만 추가하면 종속성 주입이 수정되거나 촉진되지 않습니다. 스프링은 찾아야 할 위치를 알아야 합니다.

, '이유'는 두 수 있습니다.@Autowired[ ]가 필드null

  • 당신의 클래스는 봄콩이 아닙니다.

  • 밭은 콩이 아닙니다.

업데이트: 매우 똑똑한 사람들은 이 대답을 재빨리 지적했습니다.그 때문에, 이하에 기재되어 있는 이상한 점이 설명되고 있습니다.

원래 답변:

도움이 될지는 모르겠지만, 겉으로 보기에는 옳은 일을 하면서도 같은 문제를 안고 있었습니다.Main 메서드에는 다음과 같은 코드가 있습니다.

ApplicationContext context =
    new ClassPathXmlApplicationContext(new String[] {
        "common.xml",
        "token.xml",
        "pep-config.xml" });
    TokenInitializer ti = context.getBean(TokenInitializer.class);

a a a...token.xml한 줄 파일.

<context:component-scan base-package="package.path"/>

나는 그 소포를 알아차렸다.경로가 더 이상 존재하지 않기 때문에 그냥 선을 끊어버렸습니다.

그 후, NPE가 등장하기 시작했습니다.①의 pep-config.xml방금 2콩:콩 2개밖에안 먹었어욨다.

<bean id="someAbac" class="com.pep.SomeAbac" init-method="init"/>
<bean id="settings" class="com.pep.Settings"/>

SomeAbac 클래스에는 다음과 같이 선언된 속성이 있습니다.

@Autowired private Settings settings;

알 수 없는 이유로 init에서 설정은 null입니다.<context:component-scan/>원소는 끝낼 때 이를 basePackage로 일부이다란 거짓말이 존재하지 않는, 모든 게 잘 일한다 없다.요소는 전혀 존재하지않지만 존재하며 기지 Package로 bs가 있으면 모든 것이 잘 작동합니다.이 선 지금 이:이행은 다음과 같습니다처럼 보인다.

<context:component-scan base-package="some.shit"/>

그리고 그것은 동작한다.누군가 설명해 줄 수 있을지도 모르지만, 나에게는 지금으로서는 충분합니다.)

NullPointer이것이 없으면 Null이고 Pointer를 제공하는 원인입니다를 주는 이것은 장본인이다.예외 예외.MileageFeeCalculator calc = new MileageFeeCalculator();우리는-수동으로 개체를 만들 필요가 없봄을 사용하고 있다.Spring을 사용하고 있으므로 개체를 수동으로 작성할 필요가 없습니다.개체 창조하는 코드 컨테이너로 처리될 것이다.개체 생성은하는 코드의해 처리됩니다 컨테이너에.

또한 서비스 클래스의 @Service 주석을 사용하여 필요한 bean class A를 다른 bean class B 컨스트럭터에 전달하고 @Autowired를 사용하여 클래스 B 컨스트럭터에 주석을 달 수도 있습니다.샘플 스니펫은 다음과 같습니다.

@Service
public class ClassB {

    private ClassA classA;

    @Autowired
    public ClassB(ClassA classA) {
        this.classA = classA;
    }

    public void useClassAObjectHere(){
        classA.callMethodOnObjectA();
    }
}

또한 만약 어떤 이유에서든 당신은또한 어떤 이유로든,method를 작성하면에 있는 메서드를 만든다 주목하고 있다.@Service~하듯이로final, 당신은 그것에서 액세스 할autowired 콩 항상접속하는자동전원 콩은 항상.null.

이것은 유닛 테스트의 경우에만 유효합니다.

내 서비스 수업이었다내서비스클래스에는서비스 주석이 있었고, 그것은 서비스의 평역이 있었다.@autowired다른 구성 요소 클래스입니다.다른 컴포넌트 클래슬 때 부품에 수업을 실험했다 오null.테스트했을 때 컴포넌트 클래스는 무효가 되어 있었습니다.왜냐하면 서비스 클래스에서는 다음을 사용하여 개체를 만들고 있었기 때문입니다.new

단위 를 할 , 기입을 하다를 사용하여 있지 합니다.new object()모크를 주사하다.

이것으로 문제가 해결되었습니다.여기 유용한 링크가 있습니다.

질문과는 전혀 관련이 없지만 필드 주입이 null인 경우에도 생성자 기반 주입은 정상적으로 작동합니다.

    private OrderingClient orderingClient;
    private Sales2Client sales2Client;
    private Settings2Client settings2Client;

    @Autowired
    public BrinkWebTool(OrderingClient orderingClient, Sales2Client sales2Client, Settings2Client settings2Client) {
        this.orderingClient = orderingClient;
        this.sales2Client = sales2Client;
        this.settings2Client = settings2Client;
    }

또, 입, 입, 에, a, 。static가 ''''가 됩니다'''null.

private방음음음음 method method method method method method method method method method method 가 됩니다.null 바꿔보세요.private로로 합니다.public컨트롤러에 있습니다.

언급URL : https://stackoverflow.com/questions/19896870/why-is-my-spring-autowired-field-null

반응형