Почему мое весеннее @Автоустановленное поле нулевое.

java spring null nullpointerexception autowired


Примечание: это должно быть каноническим ответом на общую проблему.

У меня есть класс Spring @Service ( MileageFeeCalculator ), который имеет поле @Autowired ( rateService ), но при попытке его использования поле становится null . Журналы показывают, что MileageFeeCalculator компонент BeanfileCalculator, и компонент MileageRateService , но я получаю mileageCharge NullPointerException всякий раз, когда пытаюсь вызвать метод m‹Charge для моего служебного компонента. Почему 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)
    ...



Answer 1 chrylis -on strike-


Поле @Autowired с комментариями имеет значение null , потому что Spring не знает о копии MileageFeeCalculator которую вы создали с new версией , и не знал, как ее автоматически связать.

Контейнер Spring Inversion of Control (IoC) имеет три основных логических компонента: реестр (называемый ApplicationContext ) компонентов (bean-компонентов), которые доступны для использования приложением, система конфигуратора, которая вводит в них зависимости объектов путем сопоставления зависимости с bean-компонентами в контексте и средство решения зависимостей, которое может просматривать конфигурацию множества различных bean-компонентов и определять, как создавать экземпляры и настраивать их в необходимом порядке.

Контейнер IoC не волшебен, и у него нет возможности узнать об объектах Java, если вы как-то не сообщите ему о них. Когда вы вызываете new , JVM создает копию нового объекта и передает ее прямо вам - она ​​никогда не проходит через процесс конфигурации. Есть три способа настроить ваши bean-компоненты.

Я разместил весь этот код, используя Spring Boot для запуска, в этом проекте GitHub ; Вы можете посмотреть на полностью работающий проект для каждого подхода, чтобы увидеть все, что вам нужно для его работы. Тег с 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);
    }
}

Если вам нужно создать новый экземпляр объекта службы для разных запросов, вы все равно можете использовать инъекцию с помощью областей действия bean-компонента Spring .

Тег, который работает путем внедрения служебного объекта @MileageFeeCalculator : working-inject-bean

Использовать @ Настраиваемый

Если вам действительно нужны объекты, созданные с помощью new , для автоматической проводки, вы можете использовать аннотацию Spring @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 может автоустанавливать,и наследственный код может вызывать,но можно напрямую спросить контекст приложения 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() и получить нужные ему 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




Answer 2 Shirish Coolkarni


Если вы не кодируете веб-приложение,убедитесь,что ваш класс,в котором @Autowiring делается,является пружинной фасолью.Обычно,весенний контейнер не будет знать о классе,который мы можем считать весенней фасолью.Мы должны рассказать весеннему контейнеру о наших весенних классах.

Это может быть достигнуто путем настройки в appln-contxt или лучше пометить класс как @Component, и не создавайте аннотированный класс с помощью оператора new. Убедитесь, что вы получаете его из 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();

    }

}



Answer 3 Ravi Durairaj


На самом деле,вы должны использовать либо JVM управляемые объекты или Spring управляемые объекты для вызова методов.из вашего вышеуказанного кода в вашем классе контроллера,вы создаете новый объект для вызова вашего класса обслуживания,который имеет объект с автоматической проводкой.

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



Answer 4 smwikipedia


Однажды я столкнулся с той же проблемой, когда не совсем привык к the life in the IoC world . Поле @Autowired одного из моих компонентов имеет значение null во время выполнения.

Основная причина заключается в том, что вместо использования автоматически созданного компонента, поддерживаемого контейнером Spring IoC (чье поле @Autowired indeed правильно введено), я newing свой собственный экземпляр этого типа компонента и использую его. Конечно, поле @Autowired этого поля равно нулю, потому что у Spring нет шансов ввести его.




Answer 5 Deepak


Ваша проблема новая (создание объектов в стиле java).

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

}