为什么Spring的@Transactional在没有代理的情况下工作?

Ant*_*rek 5 java spring hibernate jpa transactional

我对Spring的@Transactional如何在内部工作感兴趣,但在我读到它的每个地方都有一个代理概念.代理应该是自动装配代替真正的bean,并使用额外的事务处理方法"装饰"基本方法.这个理论对我来说非常清楚,并且非常有意义,因此我尝试检查它的运作方式.我创建了一个带有基本控制器和服务层的Spring Boot应用程序,并使用@Transactional注释标记了一个方法.服务看起来像这样:

public class TestService implements ITestService {

@PersistenceContext
EntityManager entityManager;

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

控制器调用服务:

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);
}}
Run Code Online (Sandbox Code Playgroud)

整个过程都有效,新实体会持久保存到DB,但我关注的重点是输出:

Service...
false
com.example.demo.TestService@7fb48179
Run Code Online (Sandbox Code Playgroud)

似乎服务类是显式注入而不是代理类.不仅"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
Run Code Online (Sandbox Code Playgroud)

先感谢您!

mer*_*ike 7

您的理解是正确的,但您的测试存在缺陷:

当spring文档说"代理"时,它们指的是模式,而不是特定的实现.Spring支持各种创建代理对象的策略.其中一个是java.lang.reflect.Proxy您测试过的,但默认情况下,spring使用更高级的技术在运行时生成一个新的类定义,该类定义了服务的实际实现类(并覆盖所有应用事务通知的方法).您可以通过检查来查看这个testService.getClass(),它将引用该生成的类,或者通过在调试器中暂停执行,并检查其中的字段targetService.

toString()引用原始对象的原因是代理toString()通过委托其目标对象来实现,目标对象使用其类名来构建String.