Поле, аннотированное @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
Если вы пытаетесь упростить задачу пользователям вашей библиотеки (или что-то еще), вы можете просто сгенерировать как WinMain
, так и main
из вашего макроса. По умолчанию компоновщик устанавливает консольные приложения для запуска в main
, а приложения win32 - с WinMain
. Другая «основная» функция будет проигнорирована.
(Предположительно, остальная часть кода не использует ни один из аргументов основной функции (argc
, argv
, hInstance
и т. Д.), , если это необходимо для работы с ними.)
Можно использовать определение _CONSOLE
, но оно не появляется автоматически; вам нужно добавить его вручную в свойства проекта. С другой стороны, выбор символа запуска является автоматическим. Таким образом, просто предоставление обеих функций и возможность выбора компоновщика могут сделать жизнь проще, потому что разработчику проекта не нужно ничего устанавливать и действительно может переключиться с Windows на консольное приложение (возможно, даже на конфигурацию), не делая этого что-нибудь.
_CONSOLE
должен сделать трюк для вас. Также вы можете выбрать подсистему, используя #pragma comment( linker, "/subsystem:windows" )
или #pragma comment( linker, "/subsystem:console" )
, если вы действительно хотите пройти этот маршрут.
Это не так, как это работает. Вам нужно написать совершенно другой код в консольном приложении и в родном приложении Windows. В консольном приложении вы используете printf или cout для создания вывода, не так много, если оно используется для мыши. Для родного приложения Windows требуется цикл сообщений и создание окна с процедурой окна, которая обнаруживает сообщение WM_PAINT для обновления окна. Etcetera.
Но вы можете написать код, который делает оба. Просто напишите как функцию main (), так и функцию WinMain (), CRT автоматически вызовет правильный.
return WinMain(0,0,0,0)
, все внутри #ifdef _CONSOLE
. Вы даже не нуждаетесь в #ifdef
.
– user
29 January 2011 в 22:54