#java #spring #spring-boot #aspectj
Вопрос:
Я попытался следовать советам, которые можно найти здесь: https://www.baeldung.com/spring-inject-bean-into-unmanaged-objects
Только для того, чтобы обнаружить, что он компилируется, но на самом деле не делает того, что должен. Компонент autowired не устанавливается в неуправляемом объекте.
@SpringBootApplication
@EnableSpringConfigured
public class SpringApp {
...
public class ApiClient {
private static String result = "not set";
//this would be called from another applicaiton written in a different language potentially.
public String apiInterface(String message) {
//This is where we're going to have to create Spring, where the languages 'join'.
SpringApplicationBuilder builder = new SpringApplicationBuilder(SpringApp.class);
builder.run();
System.out.println("Running legacy code...");
LegacyCode oldCode = new LegacyCode();
result = oldCode.doLegacyStuff("hello world");
return resu<
}
}
...
@Configurable(preConstruction = true)
public class LegacyCode {
@Autowired
MessageSender sender; //let's pretend we Spring-fied this bit of code but not the Legacy code that uses it.
public String doLegacyStuff(String message) {
sender.send(message);
sender.close();
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
return "interupted";
}
return "ok";
}
}
В этом суть кодекса. Полный код находится на github здесь:https://github.com/AlexMakesSoftware/SpringConsoleApp3
Результат, который я получаю, таков:
Exception in thread "main" java.lang.NullPointerException
at demo.LegacyCode.doLegacyStuff(LegacyCode.java:13)
at demo.ApiClient.apiInterface(ApiClient.java:17)
at demo.DummyApplication.main(DummyApplication.java:7)
Что может означать только то, что отправитель сообщений @Autowired не вводится.
Есть идеи, что я делаю не так?
ПРАВКА: Я должен отметить, что это простой пример более сложного проекта по медленной интеграции Spring в устаревшую кодовую базу. Я не могу просто «сделать все это весной», и я не могу изменить местоположение инициализации Spring, потому что этот устаревший код вызывается из другого приложения (хотя и более простого), написанного на другом языке, но работающего в JVM. Да, это ужасно, я знаю.
Ответ №1:
Я не клонировал ваш проект, просто начал быстро просматривать ваш POM в браузере. Что сразу же бросилось мне в глаза, так это:
Ваш ПОМ Maven ошибается. Вы только предварительно настраиваете плагин AspectJ Maven в pluginManagement
разделе, никогда фактически не объявляя плагин в обычном plugins
разделе. Т. е. плагин не будет использоваться во время вашей сборки.
Если после исправления у вас все еще будут проблемы с последующими действиями, я могу взглянуть еще раз.
Обновление: То, что я сказал раньше, верно, но я также заметил еще несколько упущений в вашем POM и тестовом коде:
- Вы используете несуществующую версию плагина AspectJ Maven.
- Для создания во время компиляции вам нужна среда выполнения AspectJ
aspectjrt
в пути к классам. - Вам нужно
spring-tx
в списке зависимостей, иначе класс, на который ссылается,spring-aspects
не будет найден. - Ваш тест ожидает результата, отличного от фактического «ок».
Обновление 2: Вы можете просто принять этот запрос на извлечение.
Комментарии:
1. У меня было немного времени, чтобы починить твой помпон. Пожалуйста, обратите внимание на мой обновленный ответ. Этот ответ фактически отвечает на ваш первоначальный вопрос, не указывая на обходные пути, как и другие.
Ответ №2:
Проблема в том, что :
Вы инициализируете LegacyCode
new
ключевое слово using. Теперь проблема в том, что LegacyCode
использует autowire
то, что работает только для бобов, созданных с помощью Spring. Следовательно, с NPE as @autowired
работать не new
будет .
Решение :
Вы можете пометить свое LegacyCode
«с @Component
«, а затем autowire
вставить его ApiClient
. Таким образом MessageSender
, боб будет создан и будет доступен.
Комментарии:
1. Да, я мог бы превратить все приложение в приложение Spring, это было бы решением для этого примера noddy, но не решением для огромной устаревшей кодовой базы, к которой я пытаюсь применить ту же логику. Я создал этот пример, чтобы продемонстрировать проблему, но кодовая база, с которой я работаю, ОГРОМНА, и я просто не могу заменить все «новости» на автоматические компоненты за один раз. Я бы пробыл здесь до Рождества 2030 года. Хотя спасибо.
2. Можете ли вы проверить эту ссылку docs.spring.io/spring-javaconfig/docs/1.0.0.M4/reference/html/… . Возможно, вы уже подумали об этом, но стоит отметить.
3. Я предполагаю, что вы ссылаетесь на программное обеспечение BeanFactoryAware и что я мог бы решить проблему, используя своего рода статический класс, который содержит ссылку на контекст и извлекает компонент из класса, не являющегося Spring-да, это один из способов сделать это, но не очень приятный и делает использование Spring в этом случае довольно бессмысленным.
4. Да, но в этом тоже проблема. Это тоже не совсем весна. Но я думаю, что если это не предпочтительно, то это не так.
5. Хорошо, я дам вам знать, если смогу придумать что-нибудь по этому поводу.
Ответ №3:
@SpringBootApplication
аннотации должны использоваться в вашем основном классе. Таким образом, переместите его из SpringApp
в DummyApplication
.
Комментарии:
1. Я не думаю, что в этом проблема, но в любом случае я не могу этого сделать, потому что «DummyApplicaiton» представляет приложение Scala, вызывающее мое. Я добавил больше информации выше. В любом случае, это не решение проблемы. Мне удалось заставить Spring работать ранее без устаревшего вызова и с использованием той же инициализации.
Ответ №4:
Один из способов решить эту проблему-использовать ApplicationContextAware, например:
/** In a perfect world, this wouldn't exist. Maybe one day we can spring-ify everything and this won't need to. */
@Component
public class SpringContext implements ApplicationContextAware {
private static ApplicationContext context;
public static <T extends Object> T getBean(Class<T> beanClass) {
return context.getBean(beanClass);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringContext.context = applicationContext;
}
}
Затем вы можете вызвать это в конструкторе вашего унаследованного кода, чтобы получить то, что вам нужно, чтобы так:
public LegacyCode() {
sender = SpringContext.getBean(MessageSender.class);
}
…и это работает.
Хорошо, теперь у вас есть зависимость от этого класса SpringContext, но это пройдет со временем, если вы запустите все приложение, и это не так уж плохо.