Vorne schreiben
Diese Demo erklärt, wie eine NullPointerexception durch @Transactional behebt werden kann.
https://github.com/hengyunabc/spring-boot-inside/tree/master/demo-transactional-nullpointerexception
Code zum lokalen NullPointerexception
Demo ist ein einfaches Beispiel für die Frühling -Transaktion, das die folgende StudentDao bereitstellt und @Transactional verwendet, um die Transaktion zu deklarieren:
@Component @transactionalpublic class studentDao {@autowired Private SQLSession SQLSession; public student SelectStudentbyId (Long id) {return SQLSession.Selectone ("SelectStudentbyId", ID); } Public Final Student FinaleSelectstudentbyId (Long ID) {return SQLSession.Selectone ("SelectStudentbyId", ID); }}Nach dem Start der Anwendung werden SelectStudentbyId und FinaleSelectstudentbyId wiederum aufgerufen:
@Postconstruct public void init () {studentDao.SelectstudentbyId (1); studentdao.finalselectstudentbyid (1); }Verwenden Sie MVN Spring-Boot: Führen oder importieren Sie das Projekt in die IDE, um zu starten. Die Ausnahmeinformationen sind:
Caused by: java.lang.NullPointerException at sample.mybatis.dao.StudentDao.finalSelectStudentById(StudentDao.java:27) at com.example.demo.transactional.nullpointerexception.DemoNullPointerExceptionApplication.init(DemoNullPointerExceptionApplication.java:30) at sun.reflect.nativemethodaccessorimpl.invoke0 (native Methode) bei sun.reflect.nativemethodaccessorImpl.invoke (nativemethodaccessorimpl.java:62) bei sun.reflect.delegatingMethocaccessorimpl.invoke (delegatingMethodact.invoke) in (delegatingMoDeGatingMethact. java.lang.reflect.method.invoke (method.java:498) unter org.springframework.bean.factory.Annotation org.springframework.beans.factory.annotation.initdestroyannotationBeanPostProcessor $ lifecyclemetadata.invokeInitMethods (initestroyannotationBeanPostProcessor.java:311)
Warum gibt es kein Problem bei der Ausführung von SelectStudentbyId im Anwendungscode und der Ausführung von FinalSelectstudentbyId aus einer NullPointerexception?
In derselben Bean wurde SQLSession SQLSession injiziert, und in SelectStudentbyId ist es nicht null. Warum ist Null im FinaleSelectstudentbyID -Funktion?
Holen Sie sich den Klassennamen der tatsächlichen Laufzeit
Wenn wir die beiden Funktionen vergleichen, können wir natürlich wissen, dass der Modifikator des FinalelectstudentbyID endgültig ist. Aber was ist der spezifische Grund?
Wir haben zuerst einen Haltepunkt an der Stelle gesetzt, an der die Ausnahme ausgelöst wird, den Code debuggen und die spezifische Klasse zur Laufzeit erhalten:
System.err.println (StudentDao.getClass ());
Das Druckergebnis ist:
Klasse sample.mybatis.dao.studentdao $$ EnhancerBySpringCglib $$ 210B005D
Es ist zu sehen, dass es sich um eine Klasse handelt, die von Spring AOP verarbeitet wird, aber wie lautet der spezifische Bytecode -Inhalt?
Dumpclass -Analyse
Wir verwenden das Dumpclass -Tool, um die Klasse in JVM zu entsorgen:
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
Finden Sie die Java Process PID:
$ JPS5907 DämonullPointerexceptionApplication
Entlasten Sie alle relevanten Klassen:
sudo java -jar dumpclass.jar 5907 'sample.mybatis.dao.studentdao*' /tmp /dumpresult
Demontageanalyse
Verwenden Sie JAVAP oder Graphical Tool JD-GUI, um Probe zu zersetzen.
Das Ergebnis nach der Zersetzung ist:
Klasse studentdao $$ EnhancerByspringCglib $$ 210B005D erweitert StudentDao
StudentDao$$EnhancerBySpringCGLIB$$210b005d hat keine FinalSelectstudentbyId -Inhalte
Der tatsächliche Aufruf von SelectStudentbyID ist dies. Lassen Sie uns es später tatsächlich debuggen und den spezifischen Typ sehen
public Final Student SelectStudentById (Long paramlong) {try {methodInterceptor tmp4_1 = this.cglib $ callback_0; if (tmp4_1 == null) {tmp4_1; Cglib $ bind_callbacks (this); } MethodInterceptor tmp17_14 = this.cglib $ callback_0; if (tmp17_14! = null) {Object [] tmp29_26 = neues Objekt [1]; Langes tmp35_32 = neuer java/lang/lang; Langes tmp36_35 = tmp35_32; tmp36_35; tmp36_35. <init> (paramlong); TMP29_26 [0] = TMP35_32; return (student) tmp17_14.Intercept (this, cGlib $ selectStudentbyId $ 0 $ Methode, TMP29_26, cglib $ selectStudentbyId $ 0 $ Proxy); } return Super.SelectstudentbyId (paramlong); } catch (RunTimeException | Fehler localRuntimeException) {localRuntimeException; } catch (Throwable Local throwable) {werfen neu ohne offenkundigem Drowablexception (Local throwable); }}Nehmen wir das eigentliche Debuggen. Obwohl der Code von StudentDao $$ EnhancerByspringCglib $$ $$ $$ $$ $$ $$ nicht direkt gesehen werden kann, kann er dennoch in einem einzigen Schritt ausgeführt werden.
Beim Debuggen können Sie sehen
1. Studentdao $$ EnhancerByspringCglib $$ 210B005D IS NULL
2. Die tatsächliche Art von this.cglib $ callback_0 ist cglibaoproxy $ dynamicAdviStInterceptor, in dem das ursprüngliche Zielobjekt tatsächlich gespeichert wird
3. Nachdem Cglibaopproxy $ dynamicAdviseDInterceptor von TransactionInterceptor verarbeitet wurde, wird das ursprüngliche Zielobjekt schließlich aufgerufen, das von sich selbst mit Reflexion gespeichert wurde.
Ursachen für die Ausnahme von Ausnahme
Sortieren Sie also die gesamte Analyse:
1. Nach der Verwendung von @Transactional erzeugt Spring AOP eine CGGLIB -Proxy -Klasse. StudentDao, die von @autowired in den tatsächlichen Benutzercode injiziert wurden, ist auch eine Instanz dieser Proxy -Klasse.
2. Die Proxy Class Studentdao $$ EnhancerByspringCglib $$ 210B005D, die von CGLIB generiert wird, wird von studentdao geerbt
3.Studentdao $$ EnhancerBySpringCglib $$ 210B005D IS NULL
V.
5. Es gibt also kein Problem beim Aufrufen der SelectStudentbyID -Funktion
Warum ist die SQLSession im FinaleLectstudentbyId -Funktion null und wirft dann eine NullPointerexception?
1.Studentdao $$ EnhancerBySpringCglib $$ 210B005D IS NULL
2. Der Modifikator der FinalelectstudentbyID -Funktion ist endgültig. CGlib kann diese Funktion nicht umschreiben
3. Wenn im FinaleSelectstudentbyID ausgeführt wird, ist die tatsächliche Ausführung des Codes im ursprünglichen StudentDao
V. Alle Felder darin sind null, daher wird eine Nullpointerexception geworfen.
Lösungen für das Problem
1. Am einfachsten ist es natürlich, den endgültigen Modifikator der FinalelectstudentbyID -Funktion zu entfernen
2. Es gibt einen anderen Weg. Verwenden Sie SQLSession nicht direkt in studentDao, sondern verwenden Sie die Funktion von GetsQlSession (). Auf diese Weise verarbeitet CGLIB auch GotsQlSession () und gibt das ursprüngliche Zielobjekt zurück
Zusammenfassen
1. Fehlerbehebung bei mehreren Debugging -Problemen und sehen Sie die Objektinformationen in der tatsächlichen Laufzeit an
2. Für den Bytecode der CGGlib -erzeugten Klasse können Sie das Dumpclass -Tool zum Entleeren und Zersetzen und Analyse verwenden
Zusammenfassen
Das obige ist die eingehende Untersuchung der Spring-Boot-Fehlerbehebung bei @transactional. Ich hoffe, es wird für alle hilfreich sein. Wenn Sie Fragen haben, hinterlassen Sie mir bitte eine Nachricht und der Editor wird allen rechtzeitig antworten. Vielen Dank für Ihre Unterstützung auf der Wulin.com -Website!