Весенний загрузочный пакетный процесс ItemProcessor impl autowired null [duplicate]

Один из подходов заключался бы в написании простой веб-службы Java (REST / JSON), которая использует библиотеку JODA

http://joda-time.sourceforge.net/faq.html#datediff

, чтобы вычислить разницу между двумя датами и вызвать эту службу из javascript.

Это предполагает, что ваш конец на Java.

464
задан Taryn 22 March 2017 в 17:24
поделиться

12 ответов

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

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

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

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

Ввод ваших bean-компонентов

Наиболее предпочтительным вариантом является позволить 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);
    }
}

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

Тег, который работает путем ввода объекта службы @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

Ручной поиск: не рекомендуется

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

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

507
ответ дан chrylis 15 August 2018 в 18:32
поделиться
  • 1
    Другая вещь, на которую нужно обратить внимание, заключается в создании объектов для bean-компонентов в компоненте @Configuration, где метод создания экземпляра конкретного класса bean аннотируется с помощью @Bean. – Donal Fellows 11 November 2013 в 02:12
  • 2
    @DonalFellows Я не совсем уверен, о чем вы говорите («создание» - неоднозначно). Вы говорите о проблеме с несколькими вызовами методов @Bean при использовании Spring Proxy AOP? – chrylis 11 November 2013 в 02:16
  • 3
    имел аналогичную проблему с единичным тестом, а @Configurable - это именно то, что мне нужно (и не знал). – Raibaz 22 November 2013 в 12:46
  • 4
    Привет, я сталкиваюсь с аналогичной проблемой, однако, когда я использую ваше первое предложение, мое приложение считает & quot; calc & quot; является нулевым при вызове & quot; distanceFee & quot; метод. Это как если бы он никогда не инициализировал @Autowired MileageFeeCalculator calc. Есть предположения? – Theo 14 July 2014 в 23:00
  • 5
    Я думаю, что вы должны добавить запись в верхней части своего ответа, которая объясняет, что извлечение первого компонента, корень, из которого вы все делаете, должно выполняться через ApplicationContext. Некоторые пользователи (для которых я закрыл как дубликаты) не понимают этого. – Sotirios Delimanolis 18 October 2014 в 02:39
  • 6

UPDATE: Действительно умные люди быстро указали на этот ответ , который объясняет странность, описанную ниже

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

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

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"/>

Я заметил, что пакет .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 в качестве basePackage, все работает Что ж. Теперь эта строка выглядит так:

<context:component-scan base-package="some.shit"/>

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

17
ответ дан Ahmed Ashour 15 August 2018 в 18:32
поделиться
  • 1
    Этот ответ является объяснением. <context:component-scan/> неявно разрешает <context:annotation-config/>, необходимый для работы @Autowired. – ForNeVeR 12 October 2017 в 04:55

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

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

    }

}
45
ответ дан Andrew Morton 15 August 2018 в 18:32
поделиться
  • 1
    привет, я прошел через ваше решение, это правильно. И здесь я хотел бы знать, Почему мы не создаем экземпляр аннотированного класса с использованием нового оператора, могу ли я узнать причину этого. – Ashish 29 December 2014 в 12:30
  • 2
    если u создаст объект с использованием нового, u будет обрабатывать жизненный цикл компонента, что противоречит концепции IOC. Нам нужно попросить контейнер сделать это, что делает это лучше – Shirish Coolkarni 20 August 2015 в 03:04

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

@Service
public class ClassB {

    private ClassA classA;

    @Autowired
    public ClassB(ClassA classA) {
        this.classA = classA;
    }

    public void useClassAObjectHere(){
        classA.callMethodOnObjectA();
    }
}
2
ответ дан apandey846 15 August 2018 в 18:32
поделиться
  • 1
    это сработало для меня, пожалуйста, расскажите, как это решает проблему? – CruelEngine 5 March 2018 в 13:58
  • 2
    @CruelEngine, посмотрите, что это инжектор конструктора (где вы явно устанавливаете объект) вместо того, чтобы просто использовать полевую инъекцию (в основном это делается с помощью конфигурации весны). Поэтому, если вы создаете объект ClassB, используя «новый», оператор - это еще одна область, которая не была бы видимой или автоподзадачной для ClassA. Следовательно, при вызове classB.useClassAObjectHere () будет вызывать NPE, поскольку объект classA не был автообновлен, если вы просто объявляете поле Injection. Читайте хрилис пытается объяснить то же самое. И это, почему впрыск конструктора рекомендуется для инъекций в поле. Это имеет смысл сейчас? – apandey846 11 April 2018 в 08:45
  • 3
    Теперь я понимаю . благодаря :) – CruelEngine 11 April 2018 в 12:28

Я новичок в 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;       
    }
}

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

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

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

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

7
ответ дан bluish 15 August 2018 в 18:32
поделиться
  • 1
    Этот шаблон необходим, чтобы сделать Spring beans доступным для устаревшего кода, но его следует избегать в новом коде. – chrylis 14 May 2015 в 13:06

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

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);
    }
}
19
ответ дан KIN 15 August 2018 в 18:32
поделиться
  • 1
    Это ответ. Поскольку вы создаете новый MilageFeeCalculator самостоятельно, Spring не участвует в создании экземпляра, поэтому Spring Spring не знает, что объект существует. Таким образом, он ничего не может с этим поделать, например, инъекции зависимостей. – Robert Greathouse 19 September 2017 в 17:27

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

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

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

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

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

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

3
ответ дан Newd 15 August 2018 в 18:32
поделиться

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

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

@RunWith(SpringRunner.class)
@SpringBootTest
public class MyTests {
    ....
1
ответ дан nobar 15 August 2018 в 18:32
поделиться

Другим решением было бы поставить вызов: 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()); 
    }
}
2
ответ дан Ondrej Bozek 15 August 2018 в 18:32
поделиться

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

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

21
ответ дан smwikipedia 15 August 2018 в 18:32
поделиться
  • 1
    Рамка DI создана для таких людей, как мы (для меня), которые хотят больше за меньшее время, даже если у нас меньше знаний о предмете. Аналогичным примером является то, что мы можем сделать OOP в c, но Java был создан просто для упрощения для программиста, подобного мне, чтобы избежать ошибок. – Prakash Pandey 12 July 2017 в 15:27

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

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

@Inject
Calculator myCalculator

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

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

7
ответ дан TwiN 15 August 2018 в 18:32
поделиться
17
ответ дан Ahmed Ashour 5 September 2018 в 17:42
поделиться
Другие вопросы по тегам:

Похожие вопросы: