Напишите впереди
Эта демонстрация объясняет, как устранить устранение неисправности NullPointerException, вызванное @Transactional.
https://github.com/hengyunabc/spring-boot-inside/tree/master/demo-transactional-nullpointerexception
Код для поиска NullPointerException
Демонстрация - это простой пример пружинной транзакции, который предоставляет следующий студент и использует @transactional для объявления транзакции:
@Component @TransactionAlpublic Class StudentDao {@autowired private sqlsession sqlsession; Public Student SelectStudentbyId (Long Id) {return sqlSession.selectone ("selectStudentById", id); } public final Final Student FinalSelectStudentById (Long Id) {return sqlSession.selectone ("selectStudentById", id); }}После того, как приложение будет запущено, SelectStudentById и FinalSelectStudentByID будут вызваны по очереди:
@Postconstruct public void init () {StudentDao.selectStudentById (1); StudentDao.FinalSelectStudentById (1); }Используйте MVN Spring-Boot: запустите или импортируйте проект в IDE, чтобы начать. Информация об исключении:
Вызвано: java.lang.nullpointerexception at sample.mybatis.dao.studentdao.finalselectstudentbyid (studentdao.java:27) на com.example.demo.transactional.nullpointerexception.demonullPointerExceptionApplication.initAvplication.jemberexception. sun.reflect.nativemethodaccessoriMpl.invoke0 (нативный метод) на sun.reflect.nativemethodaccessorimpl.invoke (nativemethodaccessorimpl.java:62) на sun.reflect.delegatingmethodaccessorimpl.invoke (DelegatingMethodaccesspormpl. java.lang.reflect.method.invoke (method.java:498) на org.springframework.beans.factory.annotation.initdestroyannotationbeanpostprocessor $ org.springframework.beans.factory.annotation.initdestroyantationbeanpostprocessor $ lifecyclemetadata.invokeinitmethods (initdestroyannotationbeanpostprocessor.java:311)
Почему нет проблем при выполнении SelectStudentById в коде приложения, а также выполнение FinalSelectStudentByID, бросает NullPointerException?
В той же бобах был введен SQLSession SQLSession, а в SelectStudentbyId он не нулевой. Почему NULL в функции FinalSelectStudentByID?
Получите название класса фактического времени выполнения
Конечно, когда мы сравниваем две функции, мы можем знать, что это потому, что модификатор FinalSelectStudentbyID является окончательным. Но какая конкретная причина?
Сначала мы установили точку останова в том месте, где брошено исключение, отлаживает код и получаем конкретный класс во время выполнения:
System.err.println (StudentDao.getClass ());
Результат печати:
Class Sample.mybatis.dao.studentdao $$ Enhancerbyspringcglib $$ 210b005d
Видно, что это класс, обрабатываемый Spring AOP, но каково его конкретное содержание Bytecode?
Анализ дамп -класса
Мы используем инструмент Dilmclass, чтобы сбросить класс в JVM:
https://github.com/hengyunabc/dumpclass
wget http://search.maven.org/remotecontent?filepath=io/github/hengyunabc/dumpclass/0.0.1/dumpclass-0.0.1.jar -o Dimplclass.jar
Найдите PID Process Java:
$ jps5907 DemonullPointerExceptionApplication
Сбросьте все соответствующие классы:
Sudo java -jar dilmblass.jar 5907 'sample.mybatis.dao.studentdao*' /tmp /dumpresult
Анализ разборки
Используйте Javap или графический инструмент JD-Gui, чтобы разложить sample.mybatis.dao.studentdao $$ Enhancerbyspringcglib $$ 210B005D.
Результатом после разложения является:
Class StudentDao $$ EnhancerByspringcglib $$ 210B005D расширяет студентдао
StudentDao$$EnhancerBySpringCGLIB$$210b005d не имеет контента, связанного с финаллектудбидом
Фактическим вызовом SelectStudentById является this.cglib $ callback_0, то есть Methodinterceptor TMP4_1. Давайте на самом деле отладим это позже и увидим конкретный тип
Public Final Student SelectStudentById (Long Paramlong) {try {methodInterceptor tmp4_1 = this.cglib $ callback_0; if (tmp4_1 == null) {tmp4_1; Cglib $ bind_callbacks (это); } MethodInterceptor tmp17_14 = this.cglib $ callback_0; if (tmp17_14! = null) {object [] tmp29_26 = новый объект [1]; Long tmp35_32 = new java/lang/long; Long TMP36_35 = TMP35_32; TMP36_35; tmp36_35. <int> (paramlong); TMP29_26 [0] = TMP35_32; return (студент) TMP17_14.Intercept (this, cglib $ selectStudentbyId $ 0 $ Метод, TMP29_26, cglib $ selectStudentbyId $ 0 $ Proxy); } return super.selectStudentById (paramlong); } catch (runtimeexception | error localruntimeexception) {throw localruntimeexception; } catch (throwable localthrowable) {бросить новый UndeclaredThrowableException (localThrowable); }}Давайте возьмем фактическую отладку. Хотя кодекс StudentDao $$ Enhancerbyspringcglib $$ 210B005D не может быть замечен непосредственно, его все равно можно выполнить за один шаг.
При отладке вы можете увидеть
1. Студент $$ enhancerbyspringcglib $$ 210b005d is null
2. Фактический тип этого. Cglib $ callback_0 - это cglibaopproxy $ dynamicAdvisedInterceptor, в котором оригинальный целевой объект фактически сохраняется
3. После того, как CglibaopProxy $ DynamicAdvisedInterceptor обрабатывается TransactionInterceptor, он в конечном итоге назовет исходный целевой объект, сохраненный сам по себе с отражением.
Причины бросания исключения
Так что сортируйте весь анализ:
1. После использования @Transactional Spring AOP генерирует класс прокси -сервера CGLIB. StudentDao, введенный @Autowired в фактическом коде пользователя, также является экземпляром этого класса прокси.
2. Proxy Class StudentDao $$ Enhancerbyspringcglib $$ 210B005D, созданный CGlib, унаследован от StudentDao
3. studentdao $$ Enhancerbyspringcglib $$ 210b005d - это null
4. StudentDao $$ Enhancerbyspringcglib $$ 210B005D вызывает SelectStudentByID, и на самом деле, через CglibaopProxy $ DynamicAdvisedInterceptor, он в конечном итоге вызовет оригинальный целевой объект, сохраненный сам по себе с отражением.
5. Таким образом, нет проблем в вызове функции SelectStudentByID
Так почему же SQLSession в FinalSelectStudentById функционирует NULL, а затем бросает NullPointerException?
1. studentdao $$ Enhancerbyspringcglib $$ 210B005D - это null
2. Модификатор функции FinalSelectStudentByID является окончательным, CGlib не может переписать эту функцию
3. При выполнении в FinalSelectStudentById фактическое выполнение кода в оригинальном StudentDao
4. Но объект является экземпляром StudentDao $$ Enhancerbyspringcglib $$ 210B005D. Все поля внутри него являются нулевыми, поэтому будет брошено NullpointerException.
Решения проблемы
1.
2. Есть другой способ. Не используйте SQLSession непосредственно в StudentDao, но используйте функцию getSqlSession (). Таким образом, Cglib также будет обрабатывать getSqlSession () и вернуть исходный целевой объект
Суммировать
1. Устранение неполадок с множественными проблемами отладки и просмотреть информацию о объекте в фактическом времени выполнения
2. Для байт -кода сгенерированного CGLIB класса вы можете использовать инструмент Dillclass для выброса, а затем разложить и проанализировать
Суммировать
Вышеуказанное-углубленное исследование устранения неполадок в пружине @transactional. Я надеюсь, что это будет полезно для всех. Если у вас есть какие -либо вопросы, пожалуйста, оставьте мне сообщение, и редактор ответит всем вовремя. Большое спасибо за вашу поддержку сайту wulin.com!