在这篇文章中,我们将讨论有关使用异步任务执行程序功能在不同线程中执行任务的Spring boot异步执行支持。我们将看看在Spring项目中配置SimpleAsyncTaskExecutor,ConcurrentTaskExecutor,ThreadPoolExecutor。除此之外,我们还将研究在Spring中处理异步行为时如何将实际方法返回类型封装到Future对象中。因此,让我们开始使用Spring boot异步任务执行器。<h2>引言</h2>
<blockquote>
<p style="text-align:left"><span style="color:#212529"><span style="font-family:"Varela Round",sans-serif,Helvetica,Arial">在这篇文章中,我们将讨论有关使用异步任务执行程序功能在不同线程中执行任务的Spring boot 异步执行支持。我们将看看在Spring项目中配置<code>SimpleAsyncTaskExecutor</code>,<code>ConcurrentTaskExecutor</code>,<code>ThreadPoolExecutor</code>。除此之外,我们还将研究在Spring中处理异步行为时如何将实际方法返回类型封装到<code>Future</code>对象中。因此,让我们开始使用Spring boot异步任务执行器。</span></span></p>
</blockquote>
<div style="text-align:left">
<h2 style="margin-left:0px; margin-right:0px; text-align:left">Spring中异步的配置</h2>
<p style="text-align:left"><span style="color:#212529"><span style="font-family:"Varela Round",sans-serif,Helvetica,Arial"><span style="background-color:#ffffff">要在Spring中启用异步行为,请使用<span style="color:#025969">@EnableAsync</span>注释您的配置类。</span></span></span><br />
</p>
<pre>
<code class="language-java">@EnableAsync
@SpringBootApplication
public class Application {
public static void main(String [] args){
SpringApplication.run(Application.class, args);
}
}</code></pre>
</div>
<p style="text-align:left"><span style="color:#212529"><span style="font-family:"Varela Round",sans-serif,Helvetica,Arial"><span style="background-color:#ffffff"><span style="color:#008800">@EnableAsync:</span>它检测@Async注释。</span></span></span></p>
<p style="text-align:left"><span style="color:#212529"><span style="font-family:"Varela Round",sans-serif,Helvetica,Arial"><span style="background-color:#ffffff"><span style="color:#008800">模式</span> - mode()属性控制如何应用建议。默认情况下,它的值是<span style="color:#025969">AdviceMode.PROXY</span>。请注意,如果mode()设置为<span style="color:#025969">AdviceMode.ASPECTJ</span>,则proxyTargetClass()属性的值将被忽略。还要注意,在这种情况下,spring-aspects模块JAR必须存在于类路径(classpath)中。</span></span></span></p>
<span style="color:#008800"><span style="font-family:"Varela Round",sans-serif,Helvetica,Arial"><span style="background-color:#ffffff">proxyTargetClass</span></span></span> - 它定义了使用CGLIB或JDK的代理类型,默认为CGLIB。
<h2 style="margin-left:0px; margin-right:0px; text-align:left">使用@Async注释</h2>
<p style="text-align:left"><span style="color:#212529"><span style="font-family:"Varela Round",sans-serif,Helvetica,Arial"><span style="background-color:#ffffff">此注释在方法级别上用于您希望它执行在单独线程中的那些方法。如果使用此注释标注公共方法,则此注释将按预期工作。</span></span></span></p>
<p style="text-align:left"><span style="color:#212529"><span style="font-family:"Varela Round",sans-serif,Helvetica,Arial"><span style="background-color:#ffffff">此外,该方法需要从不同的类中调用,以便它可以被代理,否则代理将被绕过。</span></span></span></p>
<p style="text-align:left"><span style="color:#212529"><span style="font-family:"Varela Round",sans-serif,Helvetica,Arial"><span style="background-color:#ffffff">以下是@Async注释方法的示例。它不会返回任何值。</span></span></span></p>
<pre>
<code class="language-java">@Override
@Async
public void createUserWithDefaultExecutor(){
//SimpleAsyncTaskExecutor
System.out.println("Currently Executing thread name - " + Thread.currentThread().getName());
System.out.println("User created with default executor");
}</code></pre>
默认情况下,Spring将搜索关联的线程池定义:或者是上下文中唯一的TaskExecutor bean,或者是一个名为“taskExecutor”的Executor bean。如果两者都不可解析,则将使用<span style="color:#008800"><span style="font-family:"Varela Round",sans-serif,Helvetica,Arial"><span style="background-color:#ffffff">SimpleAsyncTaskExecutor</span></span></span>来处理异步方法调用
<h2 style="margin-left:0px; margin-right:0px; text-align:left">在方法返回类型中使用@Async注释</h2>
<p style="text-align:left"><span style="color:#212529"><span style="font-family:"Varela Round",sans-serif,Helvetica,Arial"><span style="background-color:#ffffff">方法的实际返回类型可以包装在Future对象中。</span></span></span><br />
</p>
<pre>
<code class="language-java">@Override
@Async
public Future createAndReturnUser() {
System.out.println("Currently Executing thread name - " + Thread.currentThread().getName());
try {
User user = new User();
user.setFirstName("John");
user.setLastName("Doe");
user.setGender("Male");
Thread.sleep(5000);
return new AsyncResult(user);
} catch (InterruptedException e) {
System.out.println(e.getMessage());
}
return null;
}</code></pre>
以下是对此的测试方法。
<pre>
<code class="language-java">@Test
public void createAndReturnUserTest() throws ExecutionException, InterruptedException {
System.out.println("Current Thread in test class " + Thread.currentThread().getName());
long startTime = System.currentTimeMillis();
Future futureUser = userService.createAndReturnUser();
futureUser.get();
assertTrue((System.currentTimeMillis() - startTime) >= 5000);
}</code></pre>
<h2 style="margin-left:0px; margin-right:0px; text-align:left">在方法级定义ThreadPoolTaskExecutor和ConcurrentTaskExecutor</h2>
<p style="text-align:left"><span style="color:#212529"><span style="font-family:"Varela Round",sans-serif,Helvetica,Arial"><span style="background-color:#ffffff">默认情况下,Spring使用<span style="color:#008800">SimpleAsyncTaskExecutor</span>运行用<span style="color:#008800">@Async</span>注释的方法。我们也可以按照以下方式定义我们的自定义执行程序bean并在方法级别使用它。</span></span></span></p>
<strong>ThreadPoolTaskExecutor类</strong>
<pre>
<code class="language-java">@Bean(name = "threadPoolExecutor")
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(7);
executor.setMaxPoolSize(42);
executor.setQueueCapacity(11);
executor.setThreadNamePrefix("threadPoolExecutor-");
executor.initialize();
return executor;
}
</code></pre>
<strong>ConcurrentTaskExecutor</strong>
<pre>
<code class="language-java">@Bean(name = "ConcurrentTaskExecutor")
public TaskExecutor taskExecutor2 () {
return new ConcurrentTaskExecutor(
Executors.newFixedThreadPool(3));
}</code></pre>
这些bean可以通过以下方式在方法级别使用
<pre>
<code class="language-java">@Override
@Async("threadPoolExecutor")
public void createUserWithThreadPoolExecutor(){
System.out.println("Currently Executing thread name - " + Thread.currentThread().getName());
System.out.println("User created with thread pool executor");
}
@Override
@Async("ConcurrentTaskExecutor")
public void createUserWithConcurrentExecutor(){
System.out.println("Currently Executing thread name - " + Thread.currentThread().getName());
System.out.println("User created with concurrent task executor");
}</code></pre>
<p style="text-align:left"><span style="color:#212529"><span style="font-family:"Varela Round",sans-serif,Helvetica,Arial"><span style="background-color:#ffffff">如果你想执行一些长时间执行的任务,例如,如果你想在一天结束时压缩日志文件,SimpleAsyncTaskExecutor在情况下是有意义的。在其他情况下,如果您想每隔n秒或几分钟执行一次执行短时执行的任务,则应该使用ThreadPoolTaskExecutor,因为重用了系统资源。</span></span></span></p>
<h2 style="margin-left:0px; margin-right:0px; text-align:left">在应用程序级别实现执行程序</h2>
<p style="text-align:left"><span style="color:#212529"><span style="font-family:"Varela Round",sans-serif,Helvetica,Arial"><span style="background-color:#ffffff">要在应用程序级别实现执行程序,我们需要实现AsyncConfigurer并覆盖folowing方法。</span></span></span><br />
</p>
<pre>
<code class="language-java">@Configuration
public class AsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(7);
executor.setMaxPoolSize(42);
executor.setQueueCapacity(11);
executor.setThreadNamePrefix("MyExecutor-");
executor.initialize();
return executor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new AsyncExceptionHandler();
}
}
</code></pre>
<p style="text-align:left"><span style="color:#212529"><span style="font-family:"Varela Round",sans-serif,Helvetica,Arial"><span style="background-color:#ffffff">以下是异常处理程序。</span></span></span></p>
<strong>AsyncExceptionHandler.class</strong>
<pre>
<code class="language-java">public class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
@Override
public void handleUncaughtException(Throwable throwable, Method method, Object... obj) {
System.out.println("Exception Cause - " + throwable.getMessage());
System.out.println("Method name - " + method.getName());
for (Object param : obj) {
System.out.println("Parameter value - " + param);
}
}
}</code></pre>