Что будешь делать, если при запуске проекта все появляется MissingMethodException
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Отладка MissingMethodException при запуске проекта
MissingMethodException обычно означает: метод, который вызывается в runtime, не найден в classpath.
Это не синтаксическая ошибка (которую бы поймал компилятор), а runtime ошибка. Сталкивался с этим много раз, и у меня есть систематический подход к отладке.
Дифференциация: какой MissingMethodException?
Случай 1: NoSuchMethodException
public class Main {
public static void main(String[] args) throws Exception {
String obj = "Hello";
// Пытаемся вызвать метод который не существует
obj.getClass()
.getDeclaredMethod("nonExistentMethod") // ОПА!
.invoke(obj);
}
}
// Ошибка:
java.lang.NoSuchMethodException: java.lang.String.nonExistentMethod()
Случай 2: NoSuchMethodError
Это когда метод существовал в compile time, но пропал в runtime:
// ClassA.java (версия 1)
public class ClassA {
public void oldMethod() { }
}
// Main.java (скомпилирован против версии 1)
public class Main {
public static void main(String[] args) {
ClassA a = new ClassA();
a.oldMethod(); // Компилятор знает что метод существует
}
}
// ClassA.java (версия 2 — в runtime используется эта версия)
public class ClassA {
// oldMethod() удалили!
public void newMethod() { }
}
// Runtime error:
java.lang.NoSuchMethodError: ClassA.oldMethod()V
Этот случай самый коварный потому что компилятор не ловит ошибку.
Мой систематический подход к отладке
Шаг 1: Читаю полный stack trace
Я ВСЕГДА читаю полный stack trace, не только первую строку:
java.lang.NoSuchMethodError: org.hibernate.Session.createQuery(Ljava/lang/String;)
at com.myapp.dao.UserDao.findAll(UserDao.java:15)
at com.myapp.service.UserService.getAllUsers(UserService.java:20)
at com.myapp.api.UserController.getUsers(UserController.java:10)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
Это говорит: в UserDao.java:15 вызывается Session.createQuery(String), но она не существует.
Шаг 2: Проверяю версии dependency
Обычно это проблема версий:
# Проверяю версии в pom.xml
grep -A5 '<hibernate-core>' pom.xml
# Или через Maven
mvn dependency:tree | grep hibernate
mvn help:describe -Dartifact=org.hibernate:hibernate-core
Вывод может показать:
org.hibernate:hibernate-core:jar:4.3.11.Final:compile
Я гугл что-то в стиле: "Hibernate 4.3 Session.createQuery() signature"
И узнаю: в Hibernate 5.2 сигнатура изменилась!
Шаг 3: Проверяю конфликты версий
# Показывает граф зависимостей
mvn dependency:tree
# Может вывести что-то вроде:
[INFO] +- org.springframework.boot:spring-boot-starter-data-jpa:jar:2.4.0:compile
[INFO] | +- org.hibernate:hibernate-core:jar:5.4.25.Final:compile
[INFO] | \- org.hibernate:hibernate-commons-annotations:jar:5.1.2.Final:compile
[INFO] +- org.hibernate:hibernate-core:jar:4.3.11.Final:compile // КОНФЛИКТ!
Две разные версии одного jar'а в classpath!
Шаг 4: Проверяю что на диске (в классах)
# Распакую jar и посмотрю на метод
jar tf ~/.m2/repository/org/hibernate/hibernate-core/5.4.25.Final/hibernate-core-5.4.25.Final.jar | grep Session
# Или более прямо: смотрю classfile с помощью javap
javap -cp ~/.m2/repository/org/hibernate/hibernate-core/5.4.25.Final/hibernate-core-5.4.25.Final.jar org.hibernate.Session | grep createQuery
# Вывод:
// public <R> TypedQuery<R> createQuery(String qlString, Class<R> resultClass);
// public Query createQuery(String qlString);
Шаг 5: Проверяю что я вызываю
// Мой код
Session session = sessionFactory.openSession();
Query query = session.createQuery("SELECT u FROM User u"); // Какая версия метода?
Я проверяю в IDE: CTRL+Click на createQuery, и вижу какая версия используется во время компиляции.
Кейсы которые встречались
Кейс 1: Конфликт Hibernate версий
<!-- pom.xml —ошибка -->
<dependencies>
<!-- Spring Boot bring в Hibernate 5.4 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>2.4.0</version>
</dependency>
<!-- А потом кто-то добавил старую версию явно -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>4.3.11.Final</version> <!-- ОШИБКА! -->
</dependency>
</dependencies>
Решение:
<!-- Удалить explicit dependency на старую версию -->
<!-- Или если нужна конкретная версия -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.4.25.Final</version>
</dependency>
<!-- Или лучше: управляй через spring-boot-dependencies BOM -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.4.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Потом:
mvn clean install
# Перекомпилируй
Кейс 2: Обновили library, забыли обновить код
// Старый API (Jackson 1.x)
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(object);
// Но обновили на Jackson 2.x где метод переименован
// На Jackson 2.x это работает, но если в classpath old версия...
Решение: обновить код под новую версию.
Кейс 3: Несовместимость между разными компонентами
Spring Boot 2.7.0
↓
Requires Hibernate 5.6.x
↓
Requires HikariCP 4.0.x
↓
Requires PostgreSQL driver 42.3.x
Но developer добавил PostgreSQL driver 41.x
↓
MissingMethodError: некий метод отсутствует
Решение: используй spring-boot-dependencies BOM для правильных версий.
Кейс 4: Проблема с class loaders
В application servers (Tomcat, JBoss) могут быть проблемы:
APP A: использует commons-lang 2.5
APP B: использует commons-lang 3.0
Оба приложения на одном Tomcat'е
↓
Если class loader неправильно настроен, одно приложение
может случайно заставить другое использовать wrong версию
Мой чек-лист отладки
# 1. Получить полный stack trace
# (включить детальный лог)
# 2. Посмотреть версии зависимостей
mvn dependency:tree | grep -i <library-name>
# 3. Найти конфликты версий
mvn dependency:tree | sort | uniq -d
# 4. Проверить классе в jar'е
unzip -l ~/.m2/repository/.../library.jar | grep <ClassOrMethod>
# 5. Посмотреть сигнатуру метода
javap -cp <jar> <ClassName>
# 6. Очистить и перебилдить
mvn clean install -U # -U = force update from repositories
# 7. Проверить что скомпилировалось
mvn clean compile # Видишь compile-time ошибки?
# 8. Запустить с verbose logging
java -verbose:class MyApp # Показывает какие классы загружаются
Инструменты которыми я пользуюсь
Maven Helper плагин в IntelliJ
View → Show Maven Helper
↓
Визуально показывает граф зависимостей и конфликты
JAD decompiler
Если нужно посмотреть реальный bytecode:
jad -sjava path/to/ClassA.class
JProfile (профайлер)
Для больших приложений:
jprobes MyApp.jar
Как избежать в будущем
- Используй Spring Boot parent для управления версиями
- Exclude конфликтующие зависимости:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<exclusions>
<exclusion>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.6.0.Final</version>
</dependency>
- Тесты при запуске — включи простой smoke test:
@SpringBootTest
public class ApplicationStartupTest {
@Test
public void contextLoads() {
// Если запустилось без ошибок — хорошо
}
}
- CI/CD — build в docker'е с чистым environment
Вывод
MissingMethodException не появляется просто так. Систематический approach:
- Read full stacktrace
- Check dependency tree for conflicts
- Verify jar file contains the method
- Clean build and retry
- Use version management (BOM files)
- Add smoke tests
Для 95% случаев это проблема версий и конфликтов in classpath. Остальные 5% — это class loader проблемы в app servers.
Помни: compile-time ошибки легко видны, runtime ошибки нужно систематически отлаживать.