Почему @Transactional в Spring работает без прокси? - PullRequest
0 голосов
/ 01 сентября 2018

Меня заинтересовало, как работает SpringTree @Transactional, но везде, где я читал об этом, есть концепция прокси. Предполагается, что прокси подключается автоматически вместо реального компонента и «украшает» базовый метод дополнительными методами обработки транзакций. Теория мне понятна и имеет смысл, поэтому я попытался проверить, как она работает в действии. Я создал приложение Spring Boot с базовым контроллером и служебными слоями и пометил один метод аннотацией @Transactional. Сервис выглядит так:

public class TestService implements ITestService {

@PersistenceContext
EntityManager entityManager;

@Transactional
public void doSomething() {
    System.out.println("Service...");
    entityManager.persist(new TestEntity("XYZ"));
}}

Контроллер вызывает сервис:

public class TestController {

@Autowired
ITestService testService;

@PostMapping("/doSomething")
public ResponseEntity addHero() {
    testService.doSomething();
    System.out.println(Proxy.isProxyClass(testService.getClass()));
    System.out.println(testService);
    return new ResponseEntity(HttpStatus.OK);
}}

Все работает, новая сущность сохраняется в БД, но весь вопрос, который меня беспокоит, - это вывод:

Service...
false
com.example.demo.TestService@7fb48179

Кажется, что класс обслуживания был введен явно вместо прокси-класса. Не только «isProxy» возвращает false, но и вывод класса («com.example.demo.TestService@7fb48179») предполагает, что он не является прокси.

Не могли бы вы помочь мне с этим? Почему не был введен прокси, и как он работает даже без прокси? Есть ли способ, которым я могу «заставить» его проксировать, и если это так - почему прокси-сервер не внедряется по умолчанию в Spring?

Добавлять особо нечего, это действительно простое приложение. Свойства приложения тоже ничего особенного:

spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=superSecretPassword
spring.datasource.url=jdbc:mysql://localhost:3306/heroes?serverTimezone=UTC
spring.jpa.hibernate.ddl-auto=create-drop

Заранее спасибо!

1 Ответ

0 голосов
/ 01 сентября 2018

Ваше понимание верно, но ваш тест имеет недостатки:

Когда весенние документы говорят «прокси», они ссылаются на шаблон, а не на конкретную реализацию. Spring поддерживает различные стратегии создания прокси-объектов. Одним из них является java.lang.reflect.Proxy, который вы тестировали, но по умолчанию в Spring используется более продвинутый метод, который генерирует новое определение класса во время выполнения, которое подклассирует фактический класс реализации сервиса (и переопределяет все методы для применения рекомендаций по транзакциям). Вы можете увидеть это в действии, проверив testService.getClass(), который будет ссылаться на этот сгенерированный класс, или остановив выполнение в отладчике, и проверив поля targetService.

Причина, по которой toString() ссылается на исходный объект, заключается в том, что прокси реализует toString() путем делегирования своему целевому объекту, который использует свое имя класса для построения String.

...