0

I have the following class hierarchy in my Spring Boot application:

public abstract class A {

    private final ObjectMapper objectMapper = new ObjectMapper();

    public void method1(Object object) {
        this.objectMapper.writeValueAsString(object);
    }

}

@Component
public class B extends A {

    public void method2() {
        method1();
    }

}

in such case everything is working fine.

But when I add @Transactional(rollbackFor = Exception.class) annotation to method2() declaration, like for example:

public abstract class A {

    private final ObjectMapper objectMapper = new ObjectMapper();

    public void method1() {
        this.objectMapper; // always NULL
    }

}

@Component
public class B extends A {

    @Transactional(rollbackFor = Exception.class)
    public void method2() {
        method1();
    }

}

and execute method2() it fails with NullPointerException in method1() because of this.objectMapper is NULL;

How to let Spring correctly initialize the private final ObjectMapper objectMapper even in case of @Transactional(rollbackFor = Exception.class) is added?

Also, in case of @Transactional annotation the class of this is B$$EnhancerBySpringCGLIB and without annotation is just plain B. So, looks like CGLIB unable to properly initialize private final ObjectMapper objectMapper.. Is there any workarounds how it can be solved?

alexanoid
  • 24,051
  • 54
  • 210
  • 410
  • 1
    See https://deinum.biz/2020-07-03-Autowired-Field-Null/#aop for a broader explanation. Mainly due to proxies, remove the `final` keyword from `method2` else it won't work. – M. Deinum Aug 27 '20 at 12:30

1 Answers1

1

Remove the final in your code. You are using @Transactional with CGLIB. How CGLIB works is it creates a proxy by extending your class. Since the below method is final it cannot extend it, since final classes cannot be extended.

@Transactional(rollbackFor = Exception.class)

public final void method2() {
    method1();
}
Daniel Jacob
  • 1,455
  • 9
  • 17
  • Thanks for your answer! Is it possible to do with reflection api or something like this? Because mentioned code in my example is a part of 3rdparty lib. – alexanoid Aug 27 '20 at 12:13
  • 1
    I think you can use ReflectionUtils.setField("objectmapper",this.getClass().getSuperclass(), new ObjectMapper()); – Daniel Jacob Aug 27 '20 at 12:29