اكتب في المقدمة
يشرح هذا العرض التوضيحي كيفية استكشاف الأخطاء وإصلاحها nullpointerxception التي تسببها transactional.
https://github.com/hengyunabc/spring-boot-inside/tree/master/demo-ransactional-nullpointerxception
رمز لتحديد موقع nullpointerxception
العرض التوضيحي هو مثال بسيط للمعاملة الربيعية ، والذي يوفر الطالب التالي ويستخدم transactional لإعلان المعاملة:
@component @excedtactionalpublic class studentdao {Autowired sqlsession sqlsession ؛ الطالب العام SelectStudentByid (معرف طويل) {return sqlsession.selectone ("SelectStudentByid" ، id) ؛ } نهائيات الطالب النهائي العام في نهائيات studentByid (معرف طويل) {return sqlsession.selectone ("selectStudentByid" ، id) ؛ }}بعد بدء التطبيق ، سيتم استدعاء SelectStudentByid و InsalectStudentByid بدوره:
postconstruct public void init () {studentDao.SelectStudentByid (1) ؛ studentdao.finalsElectStudentByid (1) ؛ }استخدم mvn spring-boot: قم بتشغيل أو استيراد المشروع إلى IDE للبدء. معلومات الاستثناء التي تم طرحها هي:
بسبب: java.lang.nullpointerxception في sample.mybatis.dao.studentdao.finalselectstudentbyid (studentdao.java:27) في com.example.demo.tranctactional.nullpointerxception.demonullpoenterexceplication.init Sun.Reflect.NativeMethodAccessorImpl.Invoke0 (الطريقة الأصلية) في Sun.Reflect.NativeMethodAccessorImpl.Invoke (NativeMethodaccoriMpl.Java:62) في Sun.Reflect.DelegatingMetactorImpl.invoke (DelegatingmatorImpl. java.lang.reflect.method.invoke (method.java:498) في org.springframework org.springframework.beans.factory.annotation.initdestroyannotationbeanpostprocess $ lifecyclemetadata.invokeinitmethods (initDestroyannotationBeanpostProcessor.java:311)
لماذا لا توجد مشكلة في تنفيذ SelectStudentByid في رمز التطبيق ، وتنفيذ نهائيات EnalectStudentByid يلقي nullpointerxception؟
في نفس الفول ، تم حقن SQLSession SQLSession ، وفي مختار ، فهو غير خاطئ. لماذا NULL في نهائيات وظيفة؟
احصل على اسم الفصل لوقت التشغيل الفعلي
بالطبع ، عندما نقارن وظيفتي ، يمكننا أن نعرف أن ذلك يرجع إلى أن تعديل نهائيات inalselectstudentbyid نهائي. ولكن ما هو السبب المحدد؟
قمنا أولاً بتعيين نقطة توقف في المكان الذي يتم فيه إلقاء الاستثناء ، وتصحيح الرمز ، والحصول على الفئة المحددة في وقت التشغيل:
System.err.println (studentDao.getClass ()) ؛
نتيجة الطباعة هي:
class sample.mybatis.dao.studentdao $$ ensancerByspringCglib $$ 210B005D
يمكن ملاحظة أنه فئة معالجتها بواسطة Spring AOP ، ولكن ما هو محتوى Bytecode المحدد؟
تحليل الخباط
نستخدم أداة Dumpclass لتفريغ الفصل في 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 dumpclass.jar
ابحث عن عملية Java PID:
$ JPS5907 DemonullPointerExceptionApplication
تفريغ جميع الفئات ذات الصلة:
Sudo Java -jar Dumpclass.Jar 5907 'Sample.MyBatis.Dao.StudentDao*' /TMP /Dumpresult
تحليل التفكيك
استخدم javap أو أداة رسومية jd-gui لتحلل sample.mybatis.dao.studentdao $$ ensancerByspringCglib $$ 210b005d.
النتيجة بعد التحلل هي:
الفصل الدراسي $ $$ ensancerbyspringcglib $$ 210B005D يمتد الطالب
StudentDao$$EnhancerBySpringCGLIB$$210b005d ليس لديه محتوى من نهائيات inalselectstudentbyid
المكالمة الفعلية لـ SelectStudentByid هي this.cglib $ callback_0 ، أي ، methodInterceptor TMP4_1. دعنا في الواقع نلا ذلك لاحقًا ونرى النوع المحدد
الطالب النهائي العام 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] ؛ طويل TMP35_32 = جديد Java/Lang/Long ؛ طويل TMP36_35 = TMP35_32 ؛ TMP36_35 ؛ TMP36_35. <ING> (paramlong) ؛ TMP29_26 [0] = TMP35_32 ؛ إرجاع (الطالب) TMP17_14.Intercept (هذا ، cglib $ selectStudentByid $ 0 $ method ، tmp29_26 ، cglib $ selectStudentByid $ 0 $ proxy) ؛ } return super.selectStudentByid (paramlong) ؛ } catch (runTimeException | error localruntimeException) {throw localruntimeexception ؛ } catch (throwable localThrowable) {رمي جديد غير محدد غير قابل للمعاناة (localThrowable) ؛ }}لنأخذ تصحيح الأخطاء الفعلية. على الرغم من أنه لا يمكن رؤية مدونة StudentDao $$ ensancerBysPringCglib $$ 210B005D بشكل مباشر ، إلا أنه لا يزال من الممكن تنفيذها في خطوة واحدة.
عند التصحيح ، يمكنك أن ترى
1. studentdao $$ ensancerByspringCglib $$ 210B005D هو NULL
2. النوع الفعلي لهذا. cglib $ callback_0 هو cglibaopproxy $ dynamicadvisedor ، حيث يتم حفظ الكائن الهدف الأصلي بالفعل
3. بعد معالجة Cglibaopproxy $ DynamicAdvisedInctor بواسطة TransactionInterceptor ، فإنه سيطلق في النهاية الكائن الهدف الأصلي المحفوظ بمفرده مع الانعكاس.
أسباب رمي الاستثناء
لذا قم بفرز التحليل بأكمله:
1. بعد استخدام transactional ، ستقوم Spring AOP بإنشاء فئة وكيل CGLIB. StudentDao حقنها @autowired في رمز المستخدم الفعلي هو أيضا مثيل لفئة الوكيل هذه.
2. فئة الوكيل studentdao $$ ensancerByspringCglib $$ 210B005D التي تم إنشاؤها بواسطة CGLIB ورثت من studentdao
3.StudentDao $$ ensancerByspringCglib $$ 210B005D هو فارغ
4. studentdao $$ ensancerbyspringcglib $$ 210B005D يدعو SelectStudentByid ، وفي الواقع ، من خلال cglibaopproxy $ dynamicadviseditor ، سوف يطلق على الكائن الهدف الأصلي في نهاية المطاف المحفوظ بمفرده مع الانعكاس.
5. لذلك لا توجد مشكلة في استدعاء وظيفة SelectStudentByid
فلماذا يقوم SQLSession في وظيفة FinalectStudentByid Null ثم يرمي nullpointerxception؟
1.StudentDao $$ ensancerByspringCglib $$ 210B005D هو فارغ
2. المعدل لوظيفة نهائيات InalSelectStudentByid نهائية ، لا يوجد لدى CGLIB طريقة لإعادة كتابة هذه الوظيفة
3. عند تنفيذها في نهائيات inalselectstudentbyid ، فإن التنفيذ الفعلي للرمز في الطالب الأصلي
4. لكن الكائن هو مثيل للطلاب $ $$ ensancerBysPringCglib $$ 210B005D. جميع الحقول الموجودة داخلها هي خالية ، لذلك سيتم طرح nullpointerxception.
حلول للمشكلة
1. أسهل شيء هو بالطبع إزالة المعدل النهائي لوظيفة نهائيات inalselectstudentbyid
2. هناك طريقة أخرى. لا تستخدم SQLSession مباشرة في StudentDao ، ولكن استخدم وظيفة getSQlSession (). وبهذه الطريقة ، ستقوم CGLIB أيضًا بمعالجة GetSQlSession () وإعادة الكائن الهدف الأصلي
لخص
1. استكشاف الأخطاء النصفية المتعددة وشاهد معلومات الكائن في وقت التشغيل الفعلي
2. للحصول على رمز bytecode لفئة CGLIB التي تم إنشاؤها ، يمكنك استخدام أداة Dumpclass لتفريغها ثم تحللها وتحليلها
لخص
ما ورد أعلاه هو الدراسة المتعمقة ل spring boot استكشاف الأخطاء وإصلاحها transactional. آمل أن يكون ذلك مفيدًا للجميع. إذا كان لديك أي أسئلة ، فيرجى ترك رسالة لي وسوف يرد المحرر على الجميع في الوقت المناسب. شكرا جزيلا لدعمكم لموقع wulin.com!