Один из подходов заключался бы в написании простой веб-службы Java (REST / JSON), которая использует библиотеку JODA
http://joda-time.sourceforge.net/faq.html#datediff
, чтобы вычислить разницу между двумя датами и вызвать эту службу из javascript.
Это предполагает, что ваш конец на Java.
Поле, аннотированное @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);
}
}
Если вам нужно создать новый экземпляр объекта службы для разных запросов, вы все равно можете использовать используя области весеннего компонента .
Тег, который работает путем ввода объекта службы @MileageFeeCalculator
: working-inject-bean
Если вам действительно нужны объекты, созданные с 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
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"/>
, и она работает. Может быть, кто-то может дать объяснение, но для меня это достаточно прямо сейчас)
<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();
}
}
Вы также можете исправить эту проблему, используя аннотацию @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();
}
}
Я новичок в 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
) и статическим способом.
На самом деле, вы должны использовать управляемые объекты 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);
}
}
Я думаю, вы упустили, чтобы проинструктировать весну для сканирования классов с аннотацией.
Вы можете использовать @ComponentScan("packageToScan")
в классе конфигурации вашего весеннего приложения, чтобы проинструктировать весну для сканирования.
@Service, @Component
и т. д. аннотации добавляют мета-описание.
Spring только вводит экземпляры тех классов, которые либо создаются как bean-элементы, либо помечены аннотацией.
Классы, отмеченные аннотацией, должны быть идентифицированы весной перед инъекцией, @ComponentScan
указать весну найдите классы, отмеченные аннотацией. Когда Spring находит @Autowired
, он ищет связанный компонент и вставляет требуемый экземпляр.
Добавление только аннотации, не исправляет или облегчает инъекцию зависимостей, Spring должна знать, где искать.
Если это происходит в тестовом классе, убедитесь, что вы не забыли аннотировать класс.
Например, в Spring Boot:
@RunWith(SpringRunner.class)
@SpringBootTest
public class MyTests {
....
Другим решением было бы поставить вызов: 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());
}
}
Однажды я столкнулся с той же проблемой, когда я не совсем привык к the life in the IoC world
. Поле @Autowired
одного из моих bean-компонентов является нулевым во время выполнения.
Основная причина заключается в том, что вместо использования автоматически созданного компонента, поддерживаемого контейнером Spring IoC (чье поле @Autowired
равно indeed
] правильно введенный), я newing
мой собственный экземпляр этого типа бобов и его использование. Конечно, это поле @Autowired
равно нулю, потому что Spring не имеет возможности его ввести.
Кажется, что это редкий случай, но вот что со мной произошло:
Мы использовали @Inject
вместо @Autowired
, который является стандартом javaee, поддерживаемым Spring. В каждом месте он работал нормально, а бобы вводили правильно, а не в одно место. Инъекция компонента выглядит одинаково
@Inject
Calculator myCalculator
Наконец, мы обнаружили, что ошибка заключалась в том, что мы (фактически, функция автозавершения Eclipse) импортировали com.opensymphony.xwork2.Inject
вместо javax.inject.Inject
!
Итак, чтобы суммировать, убедитесь, что ваши аннотации (@Autowired
, @Inject
, @Service
, ...) имеют правильные пакеты!
@Configuration
, где метод создания экземпляра конкретного класса bean аннотируется с помощью@Bean
. – Donal Fellows 11 November 2013 в 02:12@Bean
при использовании Spring Proxy AOP? – chrylis 11 November 2013 в 02:16@Autowired MileageFeeCalculator calc
. Есть предположения? – Theo 14 July 2014 в 23:00ApplicationContext
. Некоторые пользователи (для которых я закрыл как дубликаты) не понимают этого. – Sotirios Delimanolis 18 October 2014 в 02:39