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

Why Spring's @Transactional works without proxy?

我对 Spring 的@Transactional 在内部是如何工作的很感兴趣,但是我在任何地方读到它时都有一个代理的概念。代理应该被自动装配以代替真正的 bean 和 "decorate" 带有额外事务处理方法的基本方法。 这个理论对我来说非常清楚并且非常有道理,所以我试图检查它是如何运作的。 我创建了一个带有基本控制器和服务层的 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

似乎是显式注入了服务 class 而不是代理 class。不仅 "isProxy" returns false,class 输出 ("com.example.demo.TestService@7fb48179") 也表明它不是代理。

你能帮我解决一下吗?为什么没有注入代理,没有代理它又如何工作?有什么办法可以 "force" 它被代理,如果是这样 - 为什么 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

提前致谢!

你的理解是正确的,但是你的测试有问题:

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

的字段

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