Почему мое поле 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 {
    ....