Deep Spring Boot: Troubleshoot @transactional-induced nullpointerexception

Source: Internet
Author: User
Tags aop modifier reflection
It 's written in front .

This demo shows how to troubleshoot a @transactional-induced nullpointerexception.

Https://github.com/hengyunabc/spring-boot-inside/tree/master/demo-Transactional-NullPointerException Positioning NullPointerException's Code

Demo is a simple spring transaction example that provides one of the following Studentdao and declares the transaction with @transactional:

@Component
@Transactional Public
class Studentdao {

    @Autowired
    private sqlsession sqlsession;

    Public Student Selectstudentbyid (long id) {return
        sqlsession.selectone ("Selectstudentbyid", id);
    }

    Public final Student Finalselectstudentbyid (long id) {return
        sqlsession.selectone ("Selectstudentbyid", id);
    }
}

After the application is started, Selectstudentbyid and Finalselectstudentbyid are called in turn:

    @PostConstruct public
    void init () {
        Studentdao.selectstudentbyid (1);
        Studentdao.finalselectstudentbyid (1);
    }

Start with mvn spring-boot:run or import the project into the IDE, and the exception message thrown is:

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 method) at Sun . reflect. Nativemethodaccessorimpl.invoke (nativemethodaccessorimpl.java:62) at Sun.reflect.DelegatingMethodAccessorImpl.invoke (delegatingmethodaccessorimpl.java:43) at Java.lang.reflect.Method.invoke (method.java:498) at Org.springframework.beans.factory.annotation.initdestroyannotationbeanpostprocessor$lifecycleelement.invoke ( initdestroyannotationbeanpostprocessor.java:366) at org.springframework.beans.factory.annotation.initdestroyannotationbeanpostprocessor$ Lifecyclemetadata.invokeinitmethods (initdestroyannotationbeanpostprocessor.java:311) 

Why is there no problem executing Selectstudentbyid in the application code, and execution Finalselectstudentbyid throw nullpointerexception?

In the same bean, obviously sqlsession sqlsession has been injected, and in Selectstudentbyid it is not null. Why is null in the Finalselectstudentbyid function? gets the class name of the actual runtime

Of course, we compare two functions to know that the Finalselectstudentbyid modifier is final. But what is the specific reason?

We first put breakpoints in the place where the exception was thrown, debug the code, get to the specific runtime of what class is:

System.err.println (Studentdao.getclass ());

The results of printing are:

Class sample.mybatis.dao.studentdao$ $EnhancerBySpringCGLIB $$210b005d

Can be seen as a class that has been handled by spring AOP, but what is its specific bytecode content. Dumpclass Analysis

We use the Dumpclass tool to dump classes in the 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

Locate the Java process PID:

$ JPS
5907 demonullpointerexceptionapplication

To dump the related classes:

sudo java-jar dumpclass.jar 5907 ' sample.mybatis.dao.studentdao* '/tmp/dumpresult
Anti-assembly analysis

JAVAP or graphical tools Jd-gui to sample.mybatis.dao.studentdao$ $EnhancerBySpringCGLIB $$210b005d.

The result of the reverse compilation is: Class studentdao$ $EnhancerBySpringCGLIB $$210b005d extends Studentdao

studentdao$ $EnhancerBySpringCGLIB $$210b005d, there is no Finalselectstudentbyid related content.

Selectstudentbyid actual call is This.cglib$callback_0, that Methodinterceptor tmp4_1, and so we actually debug, see the specific type

 public final Student Selectstudentbyid (long Paramlong) {try {Methodintercepto R 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 = new Object[1];
        Long tmp35_32 = new Java/lang/long;
        Long tmp36_35 = tmp35_32;
        tmp36_35;
        Tmp36_35.<init> (Paramlong);
        Tmp29_26[0] = tmp35_32; Return (Student) tmp17_14.intercept (this, Cglib$selectstudentbyid$0$method, tmp29_26, cglib$selectstudentbyid$0$
      Proxy);
    Return Super.selectstudentbyid (Paramlong); catch (runtimeexception|
    Error localruntimeexception) {throw localruntimeexception;
    catch (Throwable localthrowable) {throw new undeclaredthrowableexception (localthrowable); }
  }

Again, the actual debug, although studentdao$ $EnhancerBySpringCGLIB $$210b005d code is not directly visible, but can be single-step.

When you are in debug, you can see

studentdao$ $EnhancerBySpringCGLIB all field in $$210b005d is null.

This. The actual type of CGLIB$CALLBACK_0 is Cglibaopproxy$dynamicadvisedinterceptor, which actually preserves the original target object in this interceptor

Cglibaopproxy$dynamicadvisedinterceptor after transactioninterceptor processing, it will eventually invoke the reason for the exception to be thrown by the original target object you saved with reflection

So organize the whole analysis: After using the @transactional, Spring AOP generates a Cglib proxy class, and @autowired injected Studentdao in the actual user code is also an instance of this proxy class Cglib generated proxy class studentdao$ $EnhancerBySpringCGLIB $$210b005d inherited from Studentdao studentdao$ $EnhancerBySpringCGLIB $$ All field in 210b005d is null studentdao$ $EnhancerBySpringCGLIB $$210b005d in the call to Selectstudentbyid, actually through cglibaopproxy$ Dynamicadvisedinterceptor, it will eventually invoke the original target object you saved with reflection, so the Selectstudentbyid function call is fine.

Then why Sqlsession sqlsession in the Finalselectstudentbyid function is null and then throws NullPointerException. studentdao$ $EnhancerBySpringCGLIB $$210b005d All field is null Finalselectstudentbyid function modifier is final,cglib no way to rewrite this function When executed into the Finalselectstudentbyid, the actual execution is in the original Studentdao code but the object is an instance of the studentdao$ $EnhancerBySpringCGLIB $$210b005d, All of the field in it is null, so it throws nullpointerexception problem solving The simplest of course is to remove the final modifier of the Finalselectstudentbyid function There is also a way to not use sqlsession directly in Studentdao, but through the getsqlsession () function, so cglib will also process getsqlsession (), returning the original target object summary Troubleshoot multiple issues debug, look at the actual Run-time object information for Cglib generated class byte code, you can use the Dumpclass tool to dump, and then back up the analysis of the problem

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.