Почему мое поле Spring @Autowired равно null?
Примечание: это должно быть каноническим ответом на общую проблему.
у меня есть пружина @Service
класса (MileageFeeCalculator
), имеющего
12 ответов
поле аннотированный @Autowired
is null
потому что весна не знает о копия MileageFeeCalculator
С new
и не знал, чтобы autowire его.
пружинная инверсия контейнера управления (IoC) имеет три основных логических компонента: реестр (называемый ApplicationContext
) компонентов (компонентов), которые доступны для использования приложением, системой конфигуратора, которая вводит в них зависимости объектов путем сопоставления зависимостей с бобы в контексте и решатель зависимостей, который может просматривать конфигурацию многих разных бобов и определять, как создавать экземпляры и настраивать их в необходимом порядке.
контейнер IoC не волшебный, и он не может знать об объектах Java, если вы каким-то образом не сообщите ему о них. Когда вы звоните new
, JVM создает экземпляр нового объекта и передает его прямо вам-он никогда не проходит процесс настройки. Есть три способа, которыми вы можете настройте свои бобы.
я написал весь этот код, используя Spring Boot для запуска, в этот проект GitHub; вы можете посмотреть на полный запуск проекта для каждого подхода, чтобы увидеть все, что вам нужно, чтобы заставить его работать. тега NullPointerException
: nonworking
ввести свой зерен
самый предпочтительный вариант-позволить Spring autowire всем вашим бобам; это требует наименьшего количества кода и является самый обслуживаемый. Чтобы заставить autowiring работать так, как вы хотели, также autowire MileageFeeCalculator
такой:
@Controller
public class MileageFeeController {
@Autowired
private MileageFeeCalculator calc;
@RequestMapping("/mileage/{miles}")
@ResponseBody
public float mileageFee(@PathVariable int miles) {
return calc.mileageCharge(miles);
}
}
Если вам нужно создать новый экземпляр объекта службы для разных запросов, вы все равно можете использовать инъекцию с помощью область весенних бобов.
тег, который работает путем инъекций @MileageFeeCalculator
сервис объекта: working-inject-bean
Использовать @Настраиваемые!--42-->
Если вам действительно нужно объекты, созданные с помощью new
чтобы быть autowired, вы можете использовать весной @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
ручной поиск bean: не рекомендуется
этот подход подходит только для взаимодействия с устаревшим кодом в особых ситуациях. Почти всегда предпочтительнее создать класс одноэлементного адаптера, который Spring может autowire, и устаревший код могут вызывать, но можно напрямую запросить контекст приложения Spring для компонента.
для этого вам нужен класс, к которому Spring может дать ссылку на
Если вы не кодируете веб-приложение, убедитесь, что ваш класс, в котором выполняется @Autowiring, является spring bean. Как правило, Spring container не будет знать о классе, который мы могли бы подумать как spring bean. Мы должны рассказать весеннему контейнеру о наших весенних классах.
Это может быть достигнуто путем настройки в appln-contxt или лучше аннотировать класс как @Component и, пожалуйста, не создавайте аннотированный класс с помощью new оператор. Убедитесь, что вы получаете его из 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();
}
}
На самом деле для вызова методов следует использовать управляемые объекты JVM или Spring-managed Object. из приведенного выше кода в классе контроллера создается новый объект для вызова класса обслуживания, который имеет объект auto-wired.
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
. The @Autowired
поле одного из моих бобов равно null во время выполнения.
основная причина заключается в том, вместо использования автоматически созданного компонента, поддерживаемого контейнером Spring IoC (чей
ваша проблема новая (создание объекта в стиле java)
MileageFeeCalculator calc = new MileageFeeCalculator();
аннотации @Service
, @Component
, @Configuration
бобы создаются в
контекст приложения Spring при запуске сервера. Но когда мы создаем объекты
с помощью оператора new объект не регистрируется в уже созданном контексте приложения. Например, Сотрудник.класс 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());
}
}
}
}
это, кажется, редкий случай, но вот что случилось со мной:
мы @Inject
вместо @Autowired
который является стандартом javaee, поддерживаемым весной. В каждом месте он работал нормально, и бобы вводились правильно, а не в одном месте. Инъекция бобов кажется такой же
@Inject
Calculator myCalculator
наконец мы обнаружили, что ошибка заключалась в том, что мы (на самом деле, функция автоматического завершения Eclipse) импортировали com.opensymphony.xwork2.Inject
вместо javax.inject.Inject
!
Итак, убедитесь, что ваш Примечания (@Autowired
, @Inject
, @Service
,... ) имейте правильные пакеты!
Я новичок в Spring, но я обнаружил это рабочее решение. Пожалуйста, скажите мне, если это deprecable пути.
Я делаю весенний укол 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);
таким образом любой Боб может быть получен любым объектом в приложении (также intantiated с new
) и статически.
Я думаю, вы пропустили, чтобы поручить spring сканировать классы с аннотациями.
можно использовать @ComponentScan("packageToScan")
в классе конфигурации вашего приложения spring, чтобы указать spring для сканирования.
@Service, @Component
etc аннотации добавить мета-описание.
Spring только вводит экземпляры тех классов, которые либо созданы как bean, либо отмечены аннотациями.
классы, отмеченные аннотацией, должны быть идентифицированы весной до инъекции,@ComponentScan
поручить spring искать классы, отмеченные аннотацией. Когда весна найдет @Autowired
он ищет связанный компонент и вводит требуемый экземпляр.
добавление аннотации только, не исправляет или не облегчает инъекцию зависимостей, весна должна знать, где искать.
другое решение было бы положить вызов:
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 в классе обслуживания и передавая требуемый класс bean в качестве параметра другому конструктору класса beans classB и аннотировать конструктор classB с помощью @Autowired. Пример фрагмента здесь:
@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
файл у меня была строка
<context:component-scan base-package="package.path"/>
я заметил, что пакет.путь больше не существует, поэтому я просто отбросил линию навсегда.
и после этого, NPE начал приходить. В pep-config.xml
у меня было всего 2 боба:
<bean id="someAbac" class="com.pep.SomeAbac" init-method="init"/>
<bean id="settings" class="com.pep.Settings"/>
и класс SomeAbac имеет свойство, объявленное как
@Autowired private Settings settings;
по какой-то неизвестной причине настройки null в init(), когда <context:component-scan/>
элемент отсутствует вообще, но когда он присутствует и имеет некоторые bs в качестве базового пакета, все работает хорошо. Эта линия теперь выглядит так:
<context:component-scan base-package="some.shit"/>
и это работает. Может быть кто-то может дать объяснение, но для меня этого достаточно сейчас )
Если это происходит в тестовом классе, убедитесь, что вы не забыли аннотировать класс.
например,Весна Загрузки:
@RunWith(SpringRunner.class)
@SpringBootTest
public class MyTests {
....