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

时间:2017-07-03 09:18:06

标签: java spring spring-boot autowired transactional

我正在尝试在我的服务的方法中使用@Transactional注释来懒洋洋地加载字段。但是,在我的Implementation类上使用@Transactional会生成所有自动连接的字段null

这是我的实施:

@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;
 }

提前谢谢。

3 个答案:

答案 0 :(得分:7)

交易等等都是使用AOP应用的,Spring中的默认AOP机制是使用代理。使用Spring Boot时,代理模式设置基于类的代理。

您可以通过2种方式之一解决此问题。

  1. 从您的方法中删除final
  2. 通过向spring.aop.proxy-target-class=false
  3. 添加application.properties来停用基于类的代理

    现在当您获得@Transactional时,这将导致您创建UserServiceImpl的代理,确切地说是基于类的代理。会发生什么是为您的UserServiceImpl创建了一个子类,并且所有方法都被覆盖以应用TransactionInterceptor。但是,由于您的方法标记为final,因此动态创建的类不能覆盖此方法。因此,该方法查看动态创建的代理类中的字段实例,该代理类始终为null

    删除final时,可以覆盖该方法,应用该行为,它将查看正确的字段实例(实际的UserServiceImpl而不是代理)。

    当禁用基于类的代理时,您将获得一个JDK动态代理,它基本上是一个瘦包装器,它实现了服务实现的所有接口。它应用添加的行为(事务)并调用实际服务。没有所需的实际类的扩展,因此您可以代理最终方法(只要它是您的接口的一部分)。

答案 1 :(得分:0)

注意 - 您执行的方法的最终结果会产生问题,而不必使用标记为@Transnational的方法。 @Transnational注释导致动态创建代理对象。拥有final方法时,它不会在代理对象上运行。

答案 2 :(得分:0)

与Kotlin合作时,我遇到了同样的问题。当我在服务内部的方法中添加@Transactional批注时,收到一条消息,提示Methods annotated with '@Transactional' must be overridable,因此我继续将类和方法都标记为open。容易吧?好吧,不是。

尽管可以编译,但是在执行时我得到了所需的存储库为null。我可以通过两种方式解决问题:

  1. 将类及其所有方法的标记为open
open class FooService(private val barRepository: BarRepository) {
    open fun aMethod(): Bar {
        ...
    }

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

这可行,但是将所有方法都标记为open的类可能看起来有些奇怪,所以我尝试了其他方法。

  1. 声明接口:
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 {
        ...
    }
}

由于所有方法都是可重写的,因此您将无需使用open

希望这有帮助=)