在 Spring Boot
开发中,我们时常遇到需要在一个类的内部调用自己的其他方法,并且这些方法可能需要事务支持。这种场景通常发生在业务逻辑较为复杂的服务类中,其中一些操作需要确保数据的一致性和完整性。本文将以 MediaFileServiceImpl
类为例,探讨如何在 Spring Boot
中有效地使用当前类的代理类来处理内部事务。
一、场景描述 考虑一个典型的例子:在 MediaFileServiceImpl
服务类中,upload
方法需要调用 upload2Mysql
方法。这里,upload2Mysql
方法是事务性的,意味着它涉及到数据库操作,这些操作需要在一个事务中被处理。如果直接在 upload
方法中调用 upload2Mysql
,由于 Spring
的代理方式,事务管理可能不会被正确应用,因为实际上是在同一个实例内部进行方法调用,绕过了 Spring
的代理。
language-java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @Service @Slf4j public class MediaFileServiceImpl implements MediaFileService { @Lazy @Autowired private MediaFileService mediaFileService; @Override public UploadFileResultDto upload() { // ... 一些业务逻辑 ... mediaFileService.upload2Mysql(); // ... 其他业务逻辑 ... } @Transactional @Override public MediaFiles upload2Mysql() { // ... 事务性操作 ... } }
二、解决方案 1. 使用 @Lazy
(推荐) 在 MediaFileServiceImpl
类中使用 @Lazy
注解解决循环依赖问题。
language-java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @Service @Slf4j public class MediaFileServiceImpl implements MediaFileService { @Lazy @Autowired private MediaFileService mediaFileService; @Override public UploadFileResultDto upload() { // ... 一些业务逻辑 ... mediaFileService.upload2Mysql(); // ... 其他业务逻辑 ... } @Transactional @Override public MediaFiles upload2Mysql() { // ... 事务性操作 ... } }
2. 使用方法注入 方法注入是一种允许在运行时动态注入方法实现的技术。这里,我们通过一个简单的例子来说明如何应用方法注入。
language-java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @Service @Slf4j public abstract class MediaFileServiceImpl implements MediaFileService { @Override public UploadFileResultDto upload() { // ... 一些业务逻辑 ... getMediaFileService().upload2Mysql(); // ... 其他业务逻辑 ... } @Transactional @Override public MediaFiles upload2Mysql() { // ... 事务性操作 ... } @Lookup protected abstract MediaFileService getMediaFileService(); }
3. 使用 ApplicationContext
这种方法通过 ApplicationContext
获取当前类的代理。
language-java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 @Service @Slf4j public class MediaFileServiceImpl implements MediaFileService { @Autowired private ApplicationContext context; private MediaFileService getMediaFileService() { return context.getBean(MediaFileService.class); } @Override public UploadFileResultDto upload() { // ... 一些业务逻辑 ... getMediaFileService().upload2Mysql(); // ... 其他业务逻辑 ... } @Transactional @Override public MediaFiles upload2Mysql() { // ... 事务性操作 ... } }
4. 分离服务层 将事务性方法移至另一个服务类中。
language-java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 @Service @Slf4j public class MediaFileServiceImpl implements MediaFileService { @Autowired private MediaFileTransactionalService transactionalService; @Override public UploadFileResultDto upload() { // ... 一些业务逻辑 ... transactionalService.upload2Mysql(); // ... 其他业务逻辑 ... } } @Service @Transactional class MediaFileTransactionalService { public MediaFiles upload2Mysql() { // ... 事务性操作 ... } }
5. AspectJ 代理模式 使用 AspectJ 代理模式而不是默认的 JDK 动态代理。
language-java 1 2 3 4 5 6 @EnableAspectJAutoProxy(proxyTargetClass = true) @Configuration public class AppConfig { // ... 其他配置 ... }
然后,您的 MediaFileServiceImpl
类保持不变。