Почему мое Spring @Autowired поле null?

Примечание. Это будет канонический ответ для общей проблемы.

У меня есть class Spring @Service ( MileageFeeCalculator ), у которого есть поле @Autowired ( rateService ), но это поле имеет значение null когда я пытаюсь его использовать. Журналы показывают, что и компонент MileageFeeCalculator компонент MileageRateService создаются, но я получаю mileageCharge NullPointerException всякий раз, когда я пытаюсь вызвать метод mileageCharge на моем сервисном компоненте. Почему весна не автоувеличивает поле?

Класс controllerа:

 @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 является null потому что Spring не знает о копии MileageFeeCalculator которую вы создали с помощью new и не знал, чтобы его автоустанавливать.

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

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

    Я опубликовал весь этот код, используя Spring Boot для запуска, в этом проекте GitHub ; вы можете посмотреть полный рабочий проект для каждого подхода, чтобы увидеть все, что вам нужно, чтобы заставить его работать. Тег с NullPointerException : nonworking

    Внесите ваши бобы

    Самый предпочтительный вариант – позволить Spring autowire всем вашим компонентам; это требует наименьшего количества кода и является наиболее удобным для обслуживания. Чтобы сделать работу по автоустановке, как вы хотели, также выполните 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 службы @MileageFeeCalculator : working-inject-bean

    Использовать @Configurable

    Если вам действительно нужны объекты, созданные с new чтобы быть автообновленными, вы можете использовать примечание Spring @Configurable вместе с AspectJ компиляцией во время компиляции, чтобы ввести ваши объекты. Этот подход вставляет код в конструктор вашего объекта, который предупреждает Spring о том, что он создается, так что Spring может настроить новый экземпляр. Для этого требуется небольшая конфигурация в вашей сборке (например, компиляция с помощью ajc ) и включение обработчиков конфигурации среды Spring ( @EnableSpringConfigured с синтаксисом JavaConfig). Этот подход используется системой активных записей Roo, позволяя new экземплярам ваших объектов получать необходимую информацию о сохранении.

     @Service @Configurable public class MileageFeeCalculator { @Autowired private MileageRateService rateService; public float mileageCharge(final int miles) { return (miles * rateService.ratePerMile()); } } 

    Тег, который работает с помощью @Configurable на объекте службы: working-configurable

    Поиск вручную: не рекомендуется

    Этот подход подходит только для взаимодействия с устаревшим кодом в особых ситуациях. Почти всегда предпочтительнее создавать одноэлементный class адаптеров, который Spring может автонастраивать, а устаревший код может вызывать, но можно напрямую спросить контекст приложения Spring для компонента.

    Для этого вам нужен class, который 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

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

    Это может быть достигнуто путем настройки в appln-contxt или лучший способ – аннотировать class как @Component и, пожалуйста, не создавайте аннотированный class с помощью нового оператора. Убедитесь, что вы получили его из контекста Appln, как показано ниже.

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

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

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

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

     MileageFeeCalculator calc = new MileageFeeCalculator(); 

    так что это не сработает.

    Решение делает этот MileageFeeCalculator как объект с автоматической проводкой в ​​самом controllerе.

    Измените свой class controllerа, как показано ниже.

     @Controller public class MileageFeeController { @Autowired MileageFeeCalculator calc; @RequestMapping("/mileage/{miles}") @ResponseBody public float mileageFee(@PathVariable int miles) { return calc.mileageCharge(miles); } } 

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

     MileageFeeCalculator calc = new MileageFeeCalculator(); 

    С помощью annotations @Service , @Component , @Configuration компоненты создаются в
    контекст приложения Spring при запуске сервера. Но когда мы создаем объекты с использованием нового оператора, объект не регистрируется в контексте приложения, который уже создан. Например, class 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, но я нашел это рабочее решение. Скажите, пожалуйста, неужели?

    Я заставляю Spring вставлять 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; } } 

    Вы можете поместить этот код в основной class приложения, если хотите.

    Другие classы могут использовать его следующим образом:

     MyBean myBean = (MyBean)SpringUtils.ctx.getBean(MyBean.class); 

    Таким образом, любой bean-компонент может быть получен любым объектом в приложении (также запутанным с new ) и статическим способом .

    Кажется, это редкий случай, но вот что со мной произошло:

    Мы использовали @Inject вместо @Autowired который является стандартом javaee, поддерживаемым Spring. В каждом месте он работал нормально, а бобы вводили правильно, а не в одно место. Инъекция бобов кажется одинаковой

     @Inject Calculator myCalculator 

    Наконец мы обнаружили, что ошибка заключалась в том, что мы (фактически, функция com.opensymphony.xwork2.Inject Eclipse) импортировали com.opensymphony.xwork2.Inject вместо javax.inject.Inject !

    Итак, чтобы обобщить, убедитесь, что ваши annotations ( @Autowired , @Inject , @Service , …) имеют правильные пакеты!

    Я думаю, вы упустили, чтобы проинструктировать весну, чтобы сканировать classы с аннотацией.

    Вы можете использовать @ComponentScan("packageToScan") в classе конфигурации вашего весеннего приложения, чтобы проинструктировать весну для сканирования.

    @Service, @Component т. Д. Аннотация добавляет мета-описание.

    Spring только вводит экземпляры тех classов, которые либо создаются как bean, либо помечены аннотацией.

    Классы, отмеченные аннотацией, должны быть идентифицированы весной перед инъекцией, @ComponentScan проинструктируйте весенний поиск classов, отмеченных аннотацией. Когда Spring находит @Autowired он ищет связанный компонент и вводит требуемый экземпляр.

    Добавление только annotations, не устраняет или облегчает инъекцию зависимости, Spring должен знать, где искать.

    Другим решением будет вызов: SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this)
    Для конструктора MileageFeeCalculator:

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

    Вы также можете исправить эту проблему, используя аннотацию @Service в classе службы и передав требуемый bean-class classа A в качестве параметра в другой конструктор classа beans и создайте аннотацию конструктора classа B с помощью @Autowired. Пример fragmentа:

     @Service public class ClassB { private ClassA classA; @Autowired public ClassB(ClassA classA) { this.classA = classA; } public void useClassAObjectHere(){ classA.callMethodOnObjectA(); } } 

    ОБНОВЛЕНИЕ: на самом деле умные люди быстро указывали на этот ответ, что объясняет странность, описанную ниже

    ОРИГИНАЛЬНЫЙ ОТВЕТ:

    Я не знаю, помогает ли это кому-либо, но я застрял с той же проблемой, даже когда делаю то, что кажется правильным. В моем основном методе у меня есть такой код:

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

    и в файле token.xml у меня была строка

      

    Я заметил, что package.path больше не существует, поэтому я просто отбросил строку навсегда.

    И после этого NPE начал приходить. В pep-config.xml меня было всего 2 боба:

       

    и class SomeAbac имеет свойство, объявленное как

     @Autowired private Settings settings; 

    по какой-то неизвестной причине, настройки имеют значение null в init (), когда элемент вообще отсутствует, но когда он присутствует и имеет некоторые bs в качестве basePackage, все работает хорошо. Теперь эта строка выглядит так:

      

    и это работает. Может быть, кто-то может дать объяснение, но для меня этого достаточно сейчас)

    Если это происходит в тестовом classе, убедитесь, что вы не забыли аннотировать class.

    Например, в Spring Boot :

     @RunWith(SpringRunner.class) @SpringBootTest public class MyTests { .... 
    Давайте будем гением компьютера.