Примечание: это должно быть каноническим ответом на общую проблему.
У меня есть @Service
класс Spring ( MileageFeeCalculator
), который имеет @Autowired
поле ( rateService
), но поле, null
когда я пытаюсь его использовать. Журналы показывают, что и MileageFeeCalculator
бин, и MileageRateService
бин создаются, но я получаю NullPointerException
всякий раз, когда пытаюсь вызвать mileageCharge
метод моего сервисного бина. Почему 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)
...
F
вызывается внутри конструктора другого компонентаS
. В этом случае передайте необходимый bean-компонентF
в качестве параметра другомуS
конструктору bean-компонентов и аннотируйте конструкторS
with@Autowire
. Не забудьте аннотировать класс первого компонентаF
с@Component
.Ответы:
Аннотированное поле
@Autowired
объясняетсяnull
тем, что Spring не знает о копии,MileageFeeCalculator
которую вы создали,new
и не знал, как автоматически связать ее.Контейнер Spring Inversion of Control (IoC) имеет три основных логических компонента: реестр (так называемый
ApplicationContext
) компонентов (компонентов), доступных для использования приложением, система конфигуратора, которая внедряет в них зависимости объектов путем сопоставления зависимости с bean-компонентами в контексте и средство решения зависимостей, которое может просматривать конфигурацию множества различных bean-компонентов и определять, как создавать экземпляры и настраивать их в необходимом порядке.Контейнер IoC не волшебен, и у него нет возможности узнать об объектах Java, если вы как-то не сообщите ему о них. Когда вы вызываете
new
, JVM создает копию нового объекта и передает ее вам - она никогда не проходит через процесс настройки. Есть три способа настроить ваши bean-компоненты.Я разместил весь этот код, используя Spring Boot для запуска, в этом проекте GitHub ; Вы можете посмотреть на полностью работающий проект для каждого подхода, чтобы увидеть все, что вам нужно для его работы. Тег с
NullPointerException
:nonworking
Введите ваши бобы
Наиболее предпочтительный вариант - разрешить Spring автоматически связывать все ваши бины; это требует наименьшего количества кода и является наиболее поддерживаемым. Для того, чтобы автопроводка работала так, как вы хотели, также выполните автопроводку
MileageFeeCalculator
так:Если вам нужно создать новый экземпляр объекта службы для разных запросов, вы все равно можете использовать инъекцию с помощью областей действия bean-компонента Spring .
Тег, который работает путем внедрения
@MileageFeeCalculator
объекта службы:working-inject-bean
Используйте @Configurable
Если вам действительно нужны объекты, созданные с помощью
new
автоматического подключения, вы можете использовать@Configurable
аннотацию Spring вместе с переплетением во время компиляции AspectJ, чтобы внедрить ваши объекты. Этот подход вставляет код в конструктор вашего объекта, который сообщает Spring, что он создается, чтобы Spring мог сконфигурировать новый экземпляр. Это требует небольшой настройки в вашей сборке (например, компиляции сajc
) и включения обработчиков конфигурации среды выполнения Spring (@EnableSpringConfigured
с синтаксисом JavaConfig). Этот подход используется системой Roo Active Record, чтобы позволитьnew
экземплярам ваших сущностей получать необходимую информацию о постоянстве.Тег, который работает с использованием
@Configurable
объекта службы:working-configurable
Ручной поиск бобов: не рекомендуется
Этот подход подходит только для взаимодействия с устаревшим кодом в особых ситуациях. Почти всегда предпочтительнее создать класс одноэлементного адаптера, который Spring может автоматически связывать, а старый код может вызывать, но можно напрямую запросить бин в контексте приложения Spring.
Для этого вам нужен класс, на который Spring может дать ссылку на
ApplicationContext
объект:Затем ваш унаследованный код может вызывать
getContext()
и извлекать нужные ему компоненты:Тег, который работает путем ручного поиска объекта службы в контексте Spring:
working-manual-lookup
источник
@Configuration
bean-компоненте, где аннотируется метод создания экземпляра определенного класса bean-компонента@Bean
.@Bean
методов при использовании Spring Proxy AOP?@Autowired MileageFeeCalculator calc
. Какие-нибудь мысли?ApplicationContext
. Некоторые пользователи (для которых я закрыт как дубликаты) не понимают этого.Если вы не программируете веб-приложение, убедитесь, что ваш класс, в котором выполняется @Autowiring, является пружинным компонентом. Как правило, контейнер Spring не будет знать о классе, который мы могли бы представить как боб весны. Мы должны рассказать контейнеру Spring о наших классах Spring.
Это может быть достигнуто путем настройки в appln-contxt или лучше пометить класс как @Component, и, пожалуйста, не создавайте аннотированный класс с помощью оператора new. Убедитесь, что вы получаете его из Appln-context, как показано ниже.
источник
На самом деле, вы должны использовать либо управляемые объекты JVM, либо управляемый Spring объект для вызова методов. Исходя из приведенного выше кода в вашем классе контроллера, вы создаете новый объект для вызова класса обслуживания, в котором есть объект с автопроводкой.
так что это не сработает.
Решение делает этот ПробегFeeCalculator в качестве объекта с автоматическим подключением в самом контроллере.
Измените ваш класс контроллера, как показано ниже.
источник
Однажды я столкнулся с той же проблемой, когда я не совсем привык
the life in the IoC world
.@Autowired
Поле одного из моих бобов является недействительным во время выполнения.Основная причина заключается в том, что вместо использования автоматически созданного компонента, поддерживаемого контейнером Spring IoC (
@Autowired
поле которогоindeed
правильно введено), я являюсьnewing
моим собственным экземпляром этого типа компонента и использую его. Конечно, это@Autowired
поле пустое, потому что у Spring нет шансов ввести его.источник
Ваша проблема новая (создание объекта в стиле Java)
С аннотацией
@Service
,@Component
,@Configuration
бобы создаются вконтексте приложения Весна при запуске сервера. Но когда мы создаем объекты с помощью оператора new, объект не регистрируется в контексте приложения, который уже создан. Например, класс Employee.java, который я использовал.
Проверь это:
источник
Я новичок в Spring, но я обнаружил это рабочее решение. Пожалуйста, скажите мне, если это осуждаемый способ.
Я делаю Spring впрыскивать
applicationContext
в этом бобе:Вы можете поместить этот код также в основной класс приложения, если хотите.
Другие классы могут использовать это так:
Таким образом, любой компонент может быть получен любым объектом в приложении (также с помощью
new
) и статическим способом .источник
Кажется, это редкий случай, но вот что случилось со мной:
@Inject
Вместо этого мы использовали@Autowired
стандарт javaee, поддерживаемый Spring. Во всех местах он работал нормально, и бобы вводили правильно, а не в одном месте. Инъекция бобов кажется такой жеНаконец, мы обнаружили, что ошибка заключалась в том, что мы (фактически, функция автоматического завершения Eclipse) импортировали
com.opensymphony.xwork2.Inject
вместоjavax.inject.Inject
!Итак, подведем итог, убедитесь , что ваши аннотации (
@Autowired
,@Inject
,@Service
...) есть правильные пакеты!источник
Я думаю, что вы пропустили указание весной сканировать классы с аннотацией.
Вы можете использовать
@ComponentScan("packageToScan")
класс конфигурации вашего приложения пружины, чтобы дать команду пружине сканировать.@Service, @Component
и т.д. аннотации добавить мета-описание.Spring только внедряет экземпляры тех классов, которые созданы как bean-компоненты или помечены аннотацией.
Классы, помеченные аннотацией, должны быть идентифицированы пружиной перед инъекцией,
@ComponentScan
проинструктируйте весну искать классы, помеченные аннотацией. Когда Spring находит,@Autowired
он ищет связанный компонент и внедряет требуемый экземпляр.Добавление только аннотаций, не исправляющих и не облегчающих внедрение зависимостей, Spring должно знать, где искать.
источник
<context:component-scan base-package="com.mypackage"/>
в мойbeans.xml
файлЕсли это происходит в тестовом классе, убедитесь, что вы не забыли аннотировать класс.
Например, в Spring Boot :
Прошло немного времени ...
Spring Boot продолжает развиваться . Больше не требуется использовать,
@RunWith
если вы используете правильную версию JUnit .Для
@SpringBootTest
работать в одиночку стоять, вам нужно использовать@Test
с JUnit5 вместо JUnit4 .Если вы ошиблись в этой конфигурации, ваши тесты будут скомпилированы, но
@Autowired
и@Value
поля (например) будутnull
. Поскольку Spring Boot работает по волшебству, у вас может быть немного возможностей для прямой отладки этого сбоя.источник
@Value
будет нулевым при использовании сstatic
полями.Другое решение было бы поместить вызов: в
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this)
конструктор ПробегFeeCalculator, как это:
источник
ОБНОВЛЕНИЕ: действительно умные люди быстро указали на этот ответ, который объясняет странность, описанную ниже
ОРИГИНАЛЬНЫЙ ОТВЕТ:
Я не знаю, помогает ли это кому-нибудь, но я застрял с той же проблемой, даже когда делал все правильно. В моем методе Main у меня есть такой код:
и в
token.xml
файле у меня была строкаЯ заметил, что package.path больше не существует, поэтому я просто отбросил строку навсегда.
И после этого начал входить NPE. У
pep-config.xml
меня было только 2 боба:и класс SomeAbac имеет свойство, объявленное как
по какой-то неизвестной причине, настройки равны нулю в init (), когда
<context:component-scan/>
элемент вообще отсутствует, но когда он присутствует и имеет некоторые bs в качестве basePackage, все работает хорошо. Эта строка теперь выглядит так:и это работает. Может быть, кто-то может дать объяснение, но для меня этого достаточно прямо сейчас)
источник
<context:component-scan/>
неявно включает<context:annotation-config/>
необходимые для@Autowired
работы.Это является причиной предоставления исключения NullPointerException.
MileageFeeCalculator calc = new MileageFeeCalculator();
Мы используем Spring - не нужно создавать объект вручную. За созданием объекта позаботится контейнер IoC.источник
Вы также можете исправить эту проблему, используя аннотацию @Service для класса обслуживания и передавая требуемый bean-компонент classA в качестве параметра другому конструктору classB других bean-компонентов, и аннотируйте конструктор classB с помощью @Autowired. Пример фрагмента здесь:
источник
То, что не было упомянуто здесь, описано в этой статье в параграфе «Порядок исполнения».
После «изучения» того, что я должен был аннотировать класс с помощью @Component или производных @Service или @Repository (я думаю, что их больше), чтобы автоматически связать другие компоненты внутри них, я понял, что эти другие компоненты все еще были нулевыми внутри конструктора родительского компонента.
Использование @PostConstruct решает, что:
а также:
источник
Это действительно только в случае модульного теста.
В моем классе Service была аннотация службы, и это был
@autowired
другой класс компонентов. Когда я тестировал класс компонента, он становился нулевым. Потому что для класса обслуживания я создавал объект, используяnew
Если вы пишете модульный тест, убедитесь, что вы не создаете объект с помощью
new object()
. Используйте вместо этого injectMock.Это исправило мою проблему. Вот полезная ссылка
источник
Также обратите внимание, что если по какой-либо причине вы создадите метод в виде
@Service
asfinal
, всегда будут иметься автоматические компоненты, к которым вы получите доступnull
.источник
Проще говоря, есть две причины, по которым
@Autowired
поле должно бытьnull
ВАШ КЛАСС НЕ ВЕСНА.
ПОЛЕ НЕ БИН.
источник
Не совсем связанный с вопросом, но если инъекция поля равна нулю, инжекция на основе конструктора все равно будет работать нормально.
источник