实际项目中应该避免@Async注解同类调用问题

同类中调用有@Async注解的方法异步不生效,

其实@Async的这个性质在官网上已经有过说明了,官网:https://www.baeldung.com/spring-async是这样说的:

“First – let’s go over the rules – @Async has two limitations:
it must be applied to public methods only
self-invocation – calling the async method from within the same class – won’t work
The reasons are simple – 「the method needs to be public」 so that it can be proxied. And 「self-invocation doesn’t work」 because it bypasses the proxy and calls the underlying method directly.”

@Async的两个限制,第一个是必须在public修饰的情况下使用,第二个就是调用自己类上的异步方法是不起作用的。

原因:注解其实是通过代理的方式来实现异步调用的。同理@Transactional注解也是

实践

之前项目中会发生使用不当的情况:

 /**
 * @author lzq
 * @date
 */
 	public Integer delete(PointAccountDTO pointAccountDTO) {
        QueryWrapper<PointAccountDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id", pointAccountDTO.getId()).last("limit 1");
        PointAccountDO pointAccountDO = pointAccountMapper.selectOne(queryWrapper);
        int count = pointAccountMapper.delete(queryWrapper);
        if (count > 0) {
            deleteMemberAnalysisPoints(pointAccountDTO.getId());
            deleteGradeConditionPoints(pointAccountDO.getCode());
        }
        return count;
    }

    @Async
    @Transactional
    public void deleteMemberAnalysisPoints(String id){}
     
    @Async
    @Transactional
    public void deleteGradeConditionPoints(String code){}

正确使用场景,先建立异步工具类

@Component
@Slf4j
public class AsyncUtils {

    private static final ExecutorService EXECUTOR = new ThreadPoolExecutor(1, 5, 60,
            TimeUnit.MINUTES, new ArrayBlockingQueue<>(512));
    @Resource
    private AsyncTaskMapper asyncTaskMapper;

    public void execute(Runnable runnable, String param, String taskDesc) {
        AsyncTaskDO asyncTaskDO = AsyncTaskDO.builder()
                .param(param)
                .taskDesc(taskDesc)
                .build();
        asyncTaskMapper.insert(asyncTaskDO);
        EXECUTOR.submit(new TenantRunnableWrap(runnable, asyncTaskMapper, asyncTaskDO));

    }


    @Slf4j
    static class TenantRunnableWrap implements Runnable {
        private final Runnable task;
        private final AsyncTaskMapper asyncTaskMapper;
        private final AsyncTaskDO asyncTaskDO;

        public TenantRunnableWrap(Runnable task, AsyncTaskMapper asyncTaskMapper, AsyncTaskDO asyncTaskDO) {
            this.task = task;
            this.asyncTaskMapper = asyncTaskMapper;
            this.asyncTaskDO = asyncTaskDO;
        }

        @Override
        public void run() {
            try {
                task.run();
            } catch (Exception e) {
                asyncTaskDO.setExceptionDesc(e.getMessage());
                asyncTaskMapper.updateById(asyncTaskDO);
                return;
            }
            asyncTaskMapper.deleteById(asyncTaskDO);
        }
    }
}

先注入

@Autowired
private AsyncUtils asyncUtils;

注意,为了要让事务生效,一定要注入自身类

@Lazy
@Resource
PointAccountService pointAccountService;

使用

@Override
@Transactional
public Integer delete(PointAccountDTO pointAccountDTO) {
        QueryWrapper<PointAccountDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id", pointAccountDTO.getId()).last("limit 1");
        PointAccountDO pointAccountDO = pointAccountMapper.selectOne(queryWrapper);
        int count = pointAccountMapper.delete(queryWrapper);
        if (count > 0) {
            asyncUtils.execute(() -> pointAccountService.deleteMemberAnalysisPoints(pointAccountDTO.getId()), 
                    String.valueOf(pointAccountDTO.getId()),
                    "异步删除成员积分");
            asyncUtils.execute(() -> pointAccountService.deleteGradeConditionPoints(pointAccountDO.getCode()),
                    pointAccountDO.getCode(),
                    "异步删除等级积分");
//            deleteMemberAnalysisPoints(pointAccountDTO.getId());
//            deleteGradeConditionPoints(pointAccountDO.getCode());
        }
        return count;
    }

@Transactional
public void deleteGradeConditionPoints(String code) {}

在这里插入图片描述
执行是为了存表方便记录

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 使用@async注解可以在方法调用时将其作为异步任务执行。而在@async注解调用feign,则可以实现异步的远程调用服务。 在使用@async注解时,需要在方法前加上该注解,并返回Future或CompletableFuture对象。然后在方法体内,可以使用feign来调用远程服务,实现异步的远程服务调用。 使用@async注解调用feign可以提高程序并发性能,减少线程阻塞等待远程服务响应的时间。同时,由于使用了异步调用,系统可以更加容易地扩展到多个服务实例,更好地支持高并发和负载均衡。 需要注意的是,使用@async注解调用feign需要确保异步任务不会阻塞主线程。因此,在调用方法时需要使用合适的线程池,以避免线程池耗尽的情况发生。此外,需要仔细评估异步调用对于整个系统的影响,以确保异步调用不会影响系统稳定性和可靠性。 总之,使用@async注解调用feign可以提高程序的性能和可扩展性,但需要仔细权衡异步调用的影响,并确保代码的正确性和稳定性。 ### 回答2: @async注解是Spring Boot的一个注解,它指示Spring Boot应该在另一个线程异步执行方法调用。Feign是一个轻量级的HTTP客户端库,用于将RESTful服务作为接口进行访问。在Spring Boot应用程序使用Feign库时,可以使用@FeignClient注解将Feign客户端绑定到REST服务。在异步调用Feign库之前,必须在Spring Boot应用程序配置异步分组。在此之后,就可以在@async注解调用Feign库了。 具体实现步骤如下: 首先,需要为Spring Boot应用程序配置异步编组。这可以通过在定义的配置类上添加@EnableAsync和@Configuration注解来完成。这样,Spring Boot应用程序就能够在不同的线程执行方法调用。 然后,在定义的Feign客户端接口上添加@FeignClient注解,并指定REST服务的名称。接着在该接口上定义异步方法,这些方法必须添加@async注解,以通知Spring Boot应该异步执行这些方法。最后,调用异步方法时,必须通过Feign库向REST服务发出请求,以获取所需的数据。 使用@async注解调用Feign库非常简单,只需要在Spring Boot应用程序进行正确的配置,并使用Feign客户端接口定义异步方法即可。这样,应用程序就能在不同的线程异步执行Feign调用,从而提高应用程序的响应性和性能。 ### 回答3: @async注解是用于异步调用方法的注解,在调用方法时会开启一个新的线程来执行方法体的代码,从而不会阻塞主线程。而Feign是一个声明式的Web Service客户端,可以让我们更容易地调用其它微服务。 在使用@async注解调用Feign时,需要注意以下几点: 1. 定义Feign客户端并注入到调用方法所在的类。 2. 在调用方法使用@autowired注解将Feign客户端注入进来。 3. 在调用方法使用@async注解,表示异步调用。 例如,下面是一个使用@async注解调用Feign的示例代码: ``` @Service public class DemoService { @Autowired private OtherServiceClient otherServiceClient; @Async public void callOtherService() { otherServiceClient.doSomething(); } } @FeignClient(name = "other-service", url = "http://localhost:8001") public interface OtherServiceClient { @RequestMapping(value = "/do-something", method = RequestMethod.GET) void doSomething(); } ``` 在上面的代码,DemoService有一个名为callOtherService的方法,使用了@async注解,表示该方法是一个异步方法。另外,DemoService还使用了@Autowired注解将OtherServiceClient注入进来。 OtherServiceClient则是一个Feign客户端,使用@FeignClient注解将其定义为一个Feign客户端,其name表示服务名,url表示服务的URL地址。 在callOtherService方法调用了OtherServiceClient的doSomething方法,表示要调用other-service服务的/do-something接口。由于callOtherService使用了@async注解,因此doSomething方法的调用将会在一个新的线程执行,不会阻塞主线程。 总的来说,使用@async注解调用Feign可以提高代码的并发能力,避免因为调用其他服务而阻塞主线程。同时由于Feign是一个声明式的客户端,使得我们调用其他服务也变得更加简单。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值