引言
在业务处理中,有些业务使用异步的方式更为合理。比如在某个业务逻辑中,把一些数据存入到redis缓存中,缓存只是一个辅助的功能,成功或者失败对主业务并不会产生根本影响,这个过程可以通过异步的方法去进行。
Spring中通过在方法上设置@Async注解,可使得方法被异步调用。也就是说该方法会在调用时立即返回,而这个方法的实际执行交给Spring的TaskExecutor去完成。
代码示例
项目是一个普通的Spring的项目,Spring的配置文件:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:task="http://www.springframework.org/schema/task" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd"> <!-- 包扫描 --> <context:component-scan base-package="com.lzumetal.ssm"/> <!-- 执行异步任务的线程池TaskExecutor --> <task:executor id="myexecutor" pool-size="5" /> <task:annotation-driven executor="myexecutor"/> </beans>
两个Service类:
package com.lzumetal.ssm.anotation.service; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; /** * 业务Service */ @Service public class BusinessService { private static final Logger log = LoggerFactory.getLogger(BusinessService.class); @Autowired private CacheService cacheService; public void doBusiness() { log.error("start to deal with our business"); cacheService.cacheData(); log.error("comlete service operation"); } /** * 获取异步方法执行的返回值 */ public void doBusinessWithAsyncReturn() throws ExecutionException, InterruptedException { log.error("start to deal with our business"); Future<String> future = cacheService.cacheDataWithReturn(); log.error(future.get()); //future.get()方法是会阻塞的 log.error("comlete service operation"); } }
package com.lzumetal.ssm.anotation.service; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.AsyncResult; import org.springframework.stereotype.Service; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; /** * 缓存服务 */ @Service public class CacheService { private static final Logger log = LoggerFactory.getLogger(CacheService.class); @Async(value = "myexecutor") //指定执行任务的TaskExecutor public void cacheData() { try { TimeUnit.SECONDS.sleep(3L); } catch (InterruptedException e) { e.printStackTrace(); } log.error("success store the result to cache"); } @Async public Future<String> cacheDataWithReturn() { try { TimeUnit.SECONDS.sleep(3L); } catch (InterruptedException e) { e.printStackTrace(); } log.error("success store the result to cache"); //返回的结果需要通过AsyncResult这个类包装 return new AsyncResult<>("Async operation success"); } }
测试类:
package com.lzumetal.ssm.anotation.test; import com.lzumetal.ssm.anotation.service.BusinessService; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import java.util.concurrent.TimeUnit; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = {"classpath:spring-context.xml"}) public class MainTest { @Autowired private BusinessService businessService; @Test public void test() throws InterruptedException { businessService.doBusiness(); //不让主线程过早结束,否则控制台看不到异步方法中的输出内容 TimeUnit.SECONDS.sleep(5L); } @Test public void testAsyncReturn() throws Exception { businessService.doBusinessWithAsyncReturn(); TimeUnit.SECONDS.sleep(5L); } }
执行test()方法的结果:
执行testAsyncReturn()方法的结果:
@Async的使用注意点
- 返回值:不要返回值直接void;需要返回值用AsyncResult或者CompletableFuture
- 可自定义执行器并指定例如:@Async("otherExecutor")
- @Async必须不同类间调用: A类—>B类.C方法()(@Async注释在B类/方法中),如果在同一个类中调用,会变同步执行,例如:A类.B()—>A类.@Async C()。
- @Async也可以加到类,表示这个类的所有方法都是异步执行,并且方法上的注解会覆盖类上的注解。但一般不这么用!
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。