简体   繁体   English

Spring boot 2 @Transactional 注释使 Autowired 字段为空

[英]Spring boot 2 @Transactional annotation makes Autowired fields null

I'm trying to use @Transactional annotation in a method on my service to lazily load a field.我正在尝试在我的服务的方法中使用@Transactional注释来延迟加载字段。 However using @Transactional on my Implementation class makes all autowired fields null .但是,在我的实现类上使用@Transactional会使所有自动装配的字段为null

Here is my implementation :这是我的实现:

@Service
public class UserServiceImpl implements UserService {

 /**
  * DefaultMapper.
  */
 @Autowired
 private DefaultMapper defaultMapper;

 /**
  * Resource service injection.
  */
 @Autowired
 private ResourceService resourceService;

 /**
  * UserRepository.
  */
 @Autowired
 private UserRepository userRepository;

 /**
  * Jwt Factory.
  */
 @Autowired
 private JwtService jwtService;

 @Override
 @Transactional
 public final UserDto findByLogin(final String login) throws ResourceNotFoundException {
 // user repository is null here when using @Transactional
  User user = this.userRepository.findByLogin(login)
   .orElseThrow(() -> new ResourceNotFoundException(
    resourceService.getMessage(MessageBundle.EXCEPTION, "resource.notfound.user.login")
   ));
  UserDto userDto = defaultMapper.asUserDtoWithRoles(user);
  return userDto;
 }

Thank you in advance.提前谢谢你。

Transaction, amongst others, are applied using AOP, the default AOP mechanism in Spring is to use proxies.事务等都是使用 AOP 应用的,Spring 中默认的 AOP 机制是使用代理。 When using Spring Boot the proxy mode is set the class based proxies.使用 Spring Boot 时,代理模式设置为基于类的代理。

You can fix this in 1 of 2 ways.您可以通过 2 种方式中的一种来解决此问题。

  1. Remove final from your method从您的方法中删除final
  2. Disable class based proxies by adding spring.aop.proxy-target-class=false to your application.properties通过将spring.aop.proxy-target-class=falseapplication.properties来禁用基于类的代理

Now when you aded @Transactional this will lead to a proxy of your UserServiceImpl to be created, a class-based proxy to be exact.现在,当您添加@Transactional这将导致创建UserServiceImpl的代理,准确地说是基于类的代理。 What happens is that a subclass is created for your UserServiceImpl and all methods are overriden to apply the TransactionInterceptor .发生的情况是为您的UserServiceImpl创建了一个子类,并且所有方法都被覆盖以应用TransactionInterceptor However as your method is marked final the dynamically created class cannot override this method.但是,由于您的方法被标记为final ,动态创建的类无法覆盖此方法。 As a result the method looks at field instances in the dynamically created proxy class which always will be null .因此,该方法查看动态创建的代理类中的字段实例,这些实例始终为null

When removing final the method can be overridden, the behavior applied and it will look at the proper field instances (of the actual UserServiceImpl instead of the proxy).当删除final方法可以被覆盖时,应用行为,它将查看正确的字段实例(实际的UserServiceImpl而不是代理)。

When disabling class based proxies, you will get a JDK Dynamic Proxy which is basically a thin wrapper which implements all the interfaces your service implements.当禁用基于类的代理时,您将获得一个 JDK 动态代理,它基本上是一个薄包装器,它实现了您的服务实现的所有接口。 It applies the added behavior (transactions) and calls the actual service.它应用添加的行为(事务)并调用实际服务。 There is no extension of the actual class needed and as such you can proxy final methods (as long as it is part of your interface).不需要对实际类进行扩展,因此您可以代理最终方法(只要它是您的接口的一部分)。

I faced the same problem working with Kotlin.我在使用 Kotlin 时遇到了同样的问题。 When I added the @Transactional annotation to a method inside a service, I got a message saying Methods annotated with '@Transactional' must be overridable so I went ahead and marked both, the class and the method as open .当我将@Transactional注释添加到服务内的方法时,我收到一条消息,说Methods annotated with '@Transactional' must be overridable所以我继续将类和方法都标记为open Easy, right?!容易吧?! Well, not quite.嗯,不完全是。

Although this compiles, I got the required repository as null at execution time.尽管可以编译,但我在执行时将所需的存储库设为 null。 I was able to solve the problem in two ways:我能够通过两种方式解决这个问题:

  1. Mark the class and ALL of its methods as open :将类及其所有方法标记为open
open class FooService(private val barRepository: BarRepository) {
    open fun aMethod(): Bar {
        ...
    }

    @Transactional
    open fun aTransactionalMethod(): Bar {
        ...
    }
}

This works but having all the methods in a class marked with open might look a bit odd, so I tried something else.这可行,但将类中的所有方法都标记为open可能看起来有点奇怪,所以我尝试了其他方法。

  1. Declare an interface:声明一个接口:
interface IFooService {
    fun aMethod(): Bar

    fun aTransactionalMethod(): Bar
}

open class FooService(private val barRepository: BarRepository) : IFooService {
    override fun aMethod(): Bar {
        ...
    }

    @Transactional
    override fun aTransactionalMethod(): Bar {
        ...
    }
}

This way you can still use the annotation since all the methods will be overridable and you won't need to use open everywhere.这样你仍然可以使用注解,因为所有的方法都是可覆盖的,你不需要在任何地方都使用open

Hope this helps =)希望这会有所帮助 =)

Notice - final on the method you execute creates the problem, not necessary the method marked with @Transnational.注意 - 您执行的方法的 final 会产生问题,而不是用@Transnational 标记的方法。 @Transnational annotation lead to dynamic creation of proxy objects. @Transnational 注释导致动态创建代理对象。 When having final method, it will not run on the proxy objects.当有 final 方法时,它不会在代理对象上运行。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM