Spring CGLIB, инициализация транзакций и частного конечного поля

#java #spring #spring-boot #spring-data-jpa #cglib

#java #spring #spring-boot #spring-data-jpa #cglib

Вопрос:

У меня есть следующая иерархия классов в моем приложении Spring Boot:

 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();
    }

}
  

в таком случае все работает нормально.

Но когда я добавляю @Transactional(rollbackFor = Exception.class) аннотацию к method2() объявлению, например:

 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();
    }

}
  

и выполнить method2() это не удается с NullPointerException помощью method1() из-за this.objectMapper того NULL , что;

Как позволить Spring правильно инициализировать private final ObjectMapper objectMapper даже в случае @Transactional(rollbackFor = Exception.class) добавления?

Кроме того, в случае @Transactional аннотации класс this является B$$EnhancerBySpringCGLIB и без аннотации является простым B . Итак, похоже, что CGLIB не может правильно инициализироваться private final ObjectMapper objectMapper .. Есть ли какие-либо обходные пути, как это можно решить?

Комментарии:

1. Смотрите deinum.biz/2020-07-03-Autowired-Field-Null/#aop для более широкого объяснения. В основном из-за прокси, удалите final ключевое слово из method2 , иначе оно не будет работать.

Ответ №1:

Удалите final в своем коде. Вы используете @Transactional с CGLIB . Как CGLIB работает, это создает прокси, расширяя ваш класс. Поскольку приведенный ниже метод является окончательным, он не может его расширить, поскольку конечные классы не могут быть расширены.

 @Transactional(rollbackFor = Exception.class)

public final void method2() {
    method1();
}
  

Комментарии:

1. Спасибо за ваш ответ! Возможно ли это сделать с помощью reflection api или чего-то подобного? Потому что упомянутый код в моем примере является частью библиотеки 3rdparty.

2. Я думаю, вы можете использовать ReflectionUtils.SetField(«objectmapper»,this.getClass().getSuperclass(), new ObjectMapper());