- Вопрос или проблема
- Ответ или решение
- 1. Убедитесь, что @Autowired используется правильно
- 2. Убедитесь, что класс инициализируется через Spring
- 3. Измените порядок инициализации
- 4. Уберите аннотацию @Configurable
- 5. Используйте @PostConstruct для инициализации
- 6. Проверьте конфигурацию вашего Spring Boot приложения
- 7. Использование Maven
- Заключение
Вопрос или проблема
Я получил проект в новой компании, где впервые использую Spring Boot и IntelliJ IDEA. Из-за устаревшего кода здесь есть ограничения, поэтому я могу использовать версию 2022.2.5.
В моем коде есть проблема с полями, помеченными аннотацией @Autowired. Изначально код, который я использовал, выглядел следующим образом:
/**
* Потребитель параметров лицензии OFLOW.
*/
@Component
@Configurable(preConstruction = false, autowire = Autowire.BY_TYPE)
@Scope(BeanDefinition.SCOPE_SINGLETON)
public class OflowLicenseParamConsumerImpl extends CPSLicenseParamBaseImpl {
/**
* Объект менеджера настроек.
*/
@Autowired
AppSettingsManager setMgr;
/**
* Объект контекста приложения.
*/
@Autowired
OflowAppContext appCtx;
@Override
public KeyStoreParam getKeyStoreParam() {
return new OflowConsumerKeyStoreParamImpl();
}
…
Вызов метода getKeyStoreParam()
вызвал NullPointerException
. Отладчик показал, что оба объекта appCtx
и setMgr
равны null
. Чтобы избавиться от этой проблемы, я изменил код следующим образом:
/**
* Потребитель параметров лицензии OFLOW.
*/
@Component
@Configurable(preConstruction = false, autowire = Autowire.BY_TYPE)
@Scope(BeanDefinition.SCOPE_SINGLETON)
public class OflowLicenseParamConsumerImpl extends CPSLicenseParamBaseImpl {
// @Autowired
// AppSettingsManager setMgr;
// @Autowired
// OflowAppContext appCtx;
@Autowired
public OflowLicenseParamConsumerImpl(final AppSettingsManager setMgr, final OflowAppContext appCtx) {
super(setMgr, appCtx);
}
@Override
public KeyStoreParam getKeyStoreParam() {
return new OflowConsumerKeyStoreParamImpl();
}
…
На этот раз мне пришлось добавить конструктор без параметров, так как программа делает вызовы к OflowLicenseParamConsumerImpl()
:
/**
* Потребитель параметров лицензии OFLOW.
*/
@Component
@Configurable(preConstruction = false, autowire = Autowire.BY_TYPE)
@Scope(BeanDefinition.SCOPE_SINGLETON)
public class OflowLicenseParamConsumerImpl extends CPSLicenseParamBaseImpl {
// @Autowired
// AppSettingsManager setMgr;
// @Autowired
// OflowAppContext appCtx;
/**
* Стандартный конструктор
*/
public OflowLicenseParamConsumerImpl() {
}
@Autowired
public OflowLicenseParamConsumerImpl(final AppSettingsManager setMgr, final OflowAppContext appCtx) {
super(setMgr, appCtx);
}
…
Но в стандартном конструкторе были неинициализированные переменные, помеченные аннотацией @Autowired, что, конечно, неудивительно. Поскольку родительский класс CPSLicenseParamBaseImpl
является только для чтения, я добавил еще один абстрактный класс между ними:
/**
* Тип параметров лицензии CPS.
*/
public abstract class CPSLicenseParamImpl extends CPSLicenseParamBaseImpl {
/**
* Менеджер настроек приложения
*/
protected AppSettingsManager setMgr;
/**
* Контекст рабочего процесса Орландо
*/
protected OflowAppContext appCtx;
/**
* Стандартный конструктор, который должен вызываться после того, как appCtx и setMgr будут инициализированы.
*/
public CPSLicenseParamImpl() {
final String methodName = "CPSLicenseParamImpl";
assertNotNull(Strings.printNotNull(methodName, "appCtx"), appCtx);
assertNotNull(Strings.printNotNull(methodName, "setMgr"), setMgr);
}
/**
* Создает новые параметры лицензии CPS.
*
* @param setMgr менеджер настроек
* @param appCtx контекст приложения
*/
public CPSLicenseParamImpl(final AppSettingsManager setMgr, final OflowAppContext appCtx) {
this.appCtx = appCtx;
this.setMgr = setMgr;
}
}
и адаптировал код следующим образом:
/**
* Потребитель параметров лицензии OFLOW.
*/
@Component
@Configurable(preConstruction = false, autowire = Autowire.BY_TYPE)
@Scope(BeanDefinition.SCOPE_SINGLETON)
public class OflowLicenseParamConsumerImpl extends CPSLicenseParamImpl {
// @Autowired
// AppSettingsManager setMgr;
// @Autowired
// OflowAppContext appCtx;
/**
* Стандартный конструктор
*/
public OflowLicenseParamConsumerImpl() {
super();
}
@Autowired
public OflowLicenseParamConsumerImpl(final AppSettingsManager setMgr, final OflowAppContext appCtx) {
super(setMgr, appCtx);
}
…
Кроме того, по совету моего предшественника, я запустил файл build.bat
build.bat:
CLS
ECHO ./build
SET JAVA_HOME=D:\tools\oracle\java
SET mvn=D:\tools\apache-maven-3.2.5\bin\mvn.bat
SET model=D:\source\workflow\orlando-domain-model
SET oflow=D:\source\workflow\vertragsverwaltung
SET portal=D:\source\workflow\portal-commons
cmd /c D: & cd %oflow% & call %mvn% clean -DskipTests
cmd /c D: & cd %portal% & call %mvn% clean compile install -DskipTests
cmd /c D: & cd %model% & call %mvn% clean compile install -DskipTests
cmd /c D: & cd %oflow% & call %mvn% compile install -DskipTests
cd %oflow%
и выбрал “Файл” > “Очистить кэши…” для повторного открытия IntelliJ. Но все равно я снова и снова получаю ту же ошибку от измененного assertNotNull(Strings.printNotNull(methodName, "appCtx"), appCtx)
из выше:
…
2024-09-30 08:53:41,322 [RMI TCP Connection(3)-127.0.0.1] ERROR [o.s.w.c.ContextLoader] : Инициализация контекста не удалась
java.lang.AssertionError: CPSLicenseParamImpl(?): appCtx никогда не должен быть null!
at org.junit.Assert.fail(Assert.java:91) ~[com.springsource.org.junit-4.7.0.jar:na]
at org.junit.Assert.assertTrue(Assert.java:43) ~[com.springsource.org.junit-4.7.0.jar:na]
at org.junit.Assert.assertNotNull(Assert.java:524) ~[com.springsource.org.junit-4.7.0.jar:na]
at at.cps.oflow.licensing.consumer.CPSLicenseParamImpl.<init>(CPSLicenseParamImpl.java from InputFileObject:29) ~[CPSLicenseParamImpl.class:na]
at at.cps.oflow.licensing.consumer.OflowLicenseParamConsumerImpl.<init>(OflowLicenseParamConsumerImpl.java:42) ~[OflowLicenseParamConsumerImpl.class:na]
at at.cps.oflow.OflowAppContext.getLicenseParamImpl(OflowAppContext.java:346) ~[OflowAppContext.class:na]
at at.cps.portal.PortalAppContext.verifyLicense(PortalAppContext.java:98) ~[portal-commons-1.2.0.jar:na]
…
CPSLicenseParamImpl.java from InputFileObject: строка 29
— это строка, где находится assertNotNull
.
Кто-нибудь сталкивался с этой проблемой? У вас есть какие-либо советы для меня?
Правка:
Я попробовал следующее, чтобы избавиться от проблемы:
/**
* Создает новые параметры лицензии CPS.
*
* @param setMgr менеджер настроек
* @param appCtx контекст приложения
*/
public CPSLicenseParamImpl(final AppSettingsManager setMgr, final OflowAppContext appCtx) {
initialization(setMgr, appCtx);
}
@PostConstruct
private void initialization(final AppSettingsManager setMgr, final OflowAppContext appCtx) {
this.appCtx = appCtx;
this.setMgr = setMgr;
}
Благодаря замечанию @M.Deinum, @PostConstruct
можно добавлять только к методам без параметров.
Я убрал этот неверный код, и вот содержимое лог-файла:
D:\tools\apache-tomcat-6.0.53\bin\catalina.bat run
[2024-09-30 10:37:14,297] Артефакт oflow:war развернут: Ожидание соединения с сервером для начала развертывания артефакта...
Используя CATALINA_BASE: "C:\Users\<username>\AppData\Local\JetBrains\IntelliJIdea2022.2\tomcat\70124700-6990-4c5e-a690-605e8b503e65"
Используя CATALINA_HOME: "D:\tools\apache-tomcat-6.0.53"
Используя CATALINA_TMPDIR: "D:\tools\apache-tomcat-6.0.53\temp"
Используя JRE_HOME: "D:\tools\Oracle\java"
Используя CLASSPATH: "D:\tools\apache-tomcat-6.0.53\bin\bootstrap.jar"
Подключено к целевому VM, адрес: '127.0.0.1:51633', транспорт: 'socket'
30.09.2024 10:37:14 org.apache.catalina.core.AprLifecycleListener init
INFO: Загружена основанная на APR библиотека Apache Tomcat Native 1.2.12 с использованием версии APR 1.5.2.
30.09.2024 10:37:14 org.apache.catalina.core.AprLifecycleListener init
INFO: Возможности APR: IPv6 [true], sendfile [true], accept filters [false], random [true].
30.09.2024 10:37:15 org.apache.catalina.core.AprLifecycleListener initializeSSL
INFO: OpenSSL успешно инициализирован с версией OpenSSL 1.0.2k 26 Jan 2017
30.09.2024 10:37:15 org.apache.coyote.http11.Http11AprProtocol init
INFO: Инициализация Coyote HTTP/1.1 на http-8080
30.09.2024 10:37:15 org.apache.coyote.ajp.AjpAprProtocol init
INFO: Инициализация Coyote AJP/1.3 на ajp-8009
30.09.2024 10:37:15 org.apache.catalina.startup.Catalina load
INFO: Инициализация завершена за 543 мс
30.09.2024 10:37:15 org.apache.catalina.core.StandardService start
INFO: Запуск службы Catalina
30.09.2024 10:37:15 org.apache.catalina.core.StandardEngine start
INFO: Запуск сервлетного движка: Apache Tomcat/6.0.53
30.09.2024 10:37:15 org.apache.coyote.http11.Http11AprProtocol start
INFO: Запуск Coyote HTTP/1.1 на http-8080
30.09.2024 10:37:15 org.apache.coyote.ajp.AjpAprProtocol start
INFO: Запуск Coyote AJP/1.3 на ajp-8009
30.09.2024 10:37:15 org.apache.catalina.startup.Catalina start
INFO: Запуск сервера за 40 мс
Подключено к серверу
[2024-09-30 10:37:15,453] Артефакт oflow:war развернут: Артефакт разворачивается, пожалуйста, подождите...
30.09.2024 10:37:15 org.apache.catalina.loader.WebappClassLoader validateJarFile
INFO: validateJarFile(D:\source\workflow\vertragsverwaltung\target\oflow-1.7.18\WEB-INF\lib\javax.servlet-api-3.1.0.jar) - jar не загружен. Смотрите спецификацию сервлета 2.3, раздел 9.7.2. Класс, вызывающий ошибку: javax/servlet/Servlet.class
2024-09-30 10:37:22,677 [RMI TCP Connection(3)-127.0.0.1] INFO [org.zkoss] : Загрузка системного по умолчанию
2024-09-30 10:37:25,027 [RMI TCP Connection(3)-127.0.0.1] INFO [a.c.o.u.ApplicationUpdateManager] : Версия приложения: 1.7.18(10718)10718
30.09.2024 10:37:25 org.apache.catalina.startup.HostConfig deployDirectory
INFO: Развертывание веб-приложения в каталоге менеджера
2024-09-30 10:37:43,626 [RMI TCP Connection(3)-127.0.0.1] ERROR [o.s.w.c.ContextLoader] : Инициализация контекста не удалась
java.lang.AssertionError: CPSLicenseParamImpl(?): appCtx никогда не должен быть null!
at org.junit.Assert.fail(Assert.java:91) ~[com.springsource.org.junit-4.7.0.jar:na]
at org.junit.Assert.assertTrue(Assert.java:43) ~[com.springsource.org.junit-4.7.0.jar:na]
at org.junit.Assert.assertNotNull(Assert.java:524) ~[com.springsource.org.junit-4.7.0.jar:na]
at at.cps.oflow.licensing.consumer.CPSLicenseParamImpl.<init>(CPSLicenseParamImpl.java from InputFileObject:31) ~[CPSLicenseParamImpl.class:na]
at at.cps.oflow.licensing.consumer.OflowLicenseParamConsumerImpl.<init>(OflowLicenseParamConsumerImpl.java:43) ~[OflowLicenseParamConsumerImpl.class:na]
at at.cps.oflow.OflowAppContext.getLicenseParamImpl(OflowAppContext.java:346) ~[OflowAppContext.class:na]
at at.cps.portal.PortalAppContext.verifyLicense(PortalAppContext.java:98) ~[portal-commons-1.2.0.jar:na]
at at.cps.oflow.OflowAppContext.onApplicationEvent(OflowAppContext.java:238) ~[OflowAppContext.class:na]
at at.cps.oflow.OflowAppContext.onApplicationEvent(OflowAppContext.java:56) ~[spring-context-3.2.6.RELEASE.jar:3.2.6.RELEASE]
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:96) ~[spring-context-3.2.6.RELEASE.jar:3.2.6.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:334) ~[spring-context-3.2.6.RELEASE.jar:3.2.6.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:948) ~[spring-context-3.2.6.RELEASE.jar:3.2.6.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:482) ~[spring-context-3.2.6.RELEASE.jar:3.2.6.RELEASE]
at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:389) ~[spring-web-3.2.6.RELEASE.jar:3.2.6.RELEASE]
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:294) ~[spring-web-3.2.6.RELEASE.jar:3.2.6.RELEASE]
at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:112) [spring-web-3.2.6.RELEASE.jar:3.2.6.RELEASE]
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4276) [catalina.jar:6.0.53]
at org.apache.catalina.core.StandardContext.start(StandardContext.java:4779) [catalina.jar:6.0.53]
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:803) [catalina.jar:6.0.53]
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:780) [catalina.jar:6.0.53]
at org.apache.catalina.startup.HostConfig.manageApp(HostConfig.java:1432) [catalina.jar:6.0.53]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.6.0_45]
at sun.reflect.NativeMethodAccessorImpl.invoke(Native Method) ~[na:1.6.0_45]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) ~[na:1.6.0_45]
at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:297) [tomcat-coyote.jar:6.0.53]
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:836) [na:1.6.0_45]
at javax.management.remote.rmi.RMIConnectionImpl.doOperation(RMIConnectionImpl.java:1454) [na:1.6.0_45]
at javax.management.remote.rmi.RMIConnectionImpl.access$300(RMIConnectionImpl.java:74)
at javax.management.remote.rmi.RMIConnectionImpl$PrivilegedOperation.run(RMIConnectionImpl.java:1295) [na:1.6.0_45]
at java.security.AccessController.doPrivileged(Native Method) [na:1.6.0_45]
at javax.management.remote.rmi.RMIConnectionImpl.doPrivilegedOperation(RMIConnectionImpl.java:1394)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.6.0_45]
at sun.reflect.NativeMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) [na:1.6.0_45]
at java.lang.reflect.Method.invoke(Method.java:597) [na:1.6.0_45]
at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:295) [tomcat-coyote.jar:6.0.53]
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:836) [na:1.6.0_45]
at javax.management.remote.rmi.RMIConnectionImpl.invoke(JMIConnectionImpl.java:762) [1.6.0_45]
at javax.management.remote.rmi.RMIConnectionImpl.invoke(RMIConnectionImpl.java:818) [na:1.6.0_45]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.6.0_45]
at sun.reflect.NativeMethodAccessorImpl.invoke(Native Method) ~[na:1.6.0_45]
at java.lang.reflect.Method.invoke(Method.java:597) ~[na:1.6.0_45]
at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:295) [tomcat-coyote.jar:6.0.53]
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:836) [na:1.6.0_45]
at javax.management.remote.rmi.RMIConnectionImpl.invoke(RMIConnectionImpl.java:762) [1.6.0_45]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.6.0_45]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) [na:1.6.0_45]
at java.lang.reflect.Method.invoke(Method.java:597) [1.6.0_45]
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895) [na:1.6.0_45]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918) [na:1.6.0_45]
at java.lang.Thread.run(Thread.java:662) [na:1.6.0_45]
30.09.2024 10:37:51 org.apache.catalina.core.StandardContext start
СЕРЬЕЗНО: Ошибка listenerStart
30.09.2024 10:37:53 org.apache.catalina.core.StandardContext start
СЕРЬЕЗНО: Запуск контекста [/oflow] не удался из-за предыдущих ошибок
30.09.2024 10:37:53 org.apache.catalina.loader.WebappClassLoader clearReferencesJdbc
СЕРЬЕЗНО: Веб-приложение [/oflow] зарегистрировало JDBC драйвер [org.postgresql.Driver], но не смогло отменить его регистрацию, когда веб-приложение было остановлено. Чтобы предотвратить утечку памяти, JDBC драйвер был принудительно отменен.
30.09.2024 10:37:53 org.apache.catalina.loader.WebappClassLoader clearReferencesJdbc
СЕРЬЕЗНО: Веб-приложение [/oflow] зарегистрировало JDBC драйвер [com.pervasive.jdbc.v2.Driver], но не смогло отменить его регистрацию, когда веб-приложение было остановлено. Чтобы предотвратить утечку памяти, JDBC драйвер был принудительно отменен.
30.09.2024 10:37:53 org.apache.catalina.loader.WebappClassLoader clearReferencesJdbc
СЕРЬЕЗНО: Веб-приложение [/oflow] зарегистрировало JDBC драйвер [com.pervasive.jdbc.v2.Driver], но не смогло отменить его регистрацию, когда веб-приложение было остановлено. Чтобы предотвратить утечку памяти, JDBC драйвер был принудительно отменен.
30.09.2024 10:37:53 org.apache.catalina.loader.WebappClassLoader clearReferencesJdbc
СЕРЬЕЗНО: Веб-приложение [/oflow] зарегистрировало JDBC драйвер [pervasive.jdbc.PervasiveDriver], но не смогло отменить его регистрацию, когда веб-приложение было остановлено. Чтобы предотвратить утечку памяти, JDBC драйвер был принудительно отменен.
Ответ или решение
Ваша проблема с автозаполнением полей класса в Spring Boot и NullPointerException может быть связана с неправильным порядком инициализации зависимостей. Ниже приведены шаги, которые помогут вам решить эту проблему.
1. Убедитесь, что @Autowired
используется правильно
Когда вы используете аннотацию @Autowired
, Spring ожидает, что зависимости будут доступны в контексте приложения. Пожалуйста, проверьте, чтобы AppSettingsManager
и OflowAppContext
были объявлены как бины в вашем Spring-контейнере. Например, они могут быть помечены аннотацией @Component
, @Service
, или добавлены в конфигурационный класс с помощью аннотации @Bean
.
2. Убедитесь, что класс инициализируется через Spring
Ваш класс OflowLicenseParamConsumerImpl
должен быть правильно инициализирован через Spring. Если вы создаете экземпляр этого класса вручную (например, через new OflowLicenseParamConsumerImpl()
), Spring не сможет инжектировать зависимости. Убедитесь, что класс создается через Spring, например, инжектируя его в другой компонент:
@Component
public class SomeOtherComponent {
private final OflowLicenseParamConsumerImpl consumer;
@Autowired
public SomeOtherComponent(OflowLicenseParamConsumerImpl consumer) {
this.consumer = consumer;
}
}
3. Измените порядок инициализации
Если у вас есть конструктор без параметров, он вызывается до того, как Spring сможет инициализировать бины, что приводит к значению null
. Убедитесь, что в родительском классе CPSLicenseParamBaseImpl
не вызываются методы, которые требуют инициализации полей appCtx
и setMgr
до того, как они были установлены.
4. Уберите аннотацию @Configurable
Аннотация @Configurable
используется в контексте аспектно-ориентированного программирования (AOP) и может вызывать проблемы в ситуации, когда инъекция бинов не выполнена как следует. Если возможно, уберите эту аннотацию для упрощения вашего кода.
5. Используйте @PostConstruct
для инициализации
Если вы хотите убедиться, что ваши поля setMgr
и appCtx
инициализированы, вы можете использовать @PostConstruct
в методе инициализации. Однако помните, что сам метод не должен принимать параметры, так как Spring не сможет инжектировать их в метод:
@PostConstruct
public void init() {
assertNotNull("appCtx не должен быть null", appCtx);
assertNotNull("setMgr не должен быть null", setMgr);
}
6. Проверьте конфигурацию вашего Spring Boot приложения
Если ваше приложение на Spring Boot не может обнаружить соответствующие конфигурации или классы, бины не будут правильно инжектироваться. Убедитесь, что ваш основной класс приложения имеет аннотацию @SpringBootApplication
и что все компоненты находятся в пределах сканирования компонентов.
7. Использование Maven
Если вы используете Maven для сборки вашего проекта, убедитесь, что зависимости правильно настроены и что нет конфликтов версий зависимостей, которые могут приводить к непредвиденным ошибкам.
Заключение
Используя вышеприведенные рекомендации и изменения, вы сможете наладить процесс автозаполнения полей в классе. Тщательно проверьте порядок инициализации зависимостей и избегайте создания экземпляров классов вручную, чтобы гарантировать, что Spring правильно управляет их жизненным циклом. Если проблема сохраняется, рекомендуется проверить логи и искать дополнительные подсказки о том, что может быть неправильно настроено в вашем контексте Spring.