Spring中@Async用法详解及简单实例

Spring中@Async用法

引言: 在Java应用中,绝大多数情况下都是通过同步的方式来实现交互处理的;但是在处理与第三方系统交互的时候,容易造成响应迟缓的情况,之前大部分都是使用多线程来完成此类任务,其实,在spring 3.x之后,就已经内置了@Async来完美解决这个问题,本文将完成介绍@Async的用法。

1.  何为异步调用?

在解释异步调用之前,我们先来看同步调用的定义;同步就是整个处理过程顺序执行,当各个过程都执行完毕,并返回结果。 异步调用则是只是发送了调用的指令,调用者无需等待被调用的方法完全执行完毕;而是继续执行下面的流程。

例如, 在某个调用中,需要顺序调用 A, B, C三个过程方法;如他们都是同步调用,则需要将他们都顺序执行完毕之后,方算作过程执行完毕; 如B为一个异步的调用方法,则在执行完A之后,调用B,并不等待B完成,而是执行开始调用C,待C执行完毕之后,就意味着这个过程执行完毕了。

2.  常规的异步调用处理方式

在Java中,一般在处理类似的场景之时,都是基于创建独立的线程去完成相应的异步调用逻辑,通过主线程和不同的线程之间的执行流程,从而在启动独立的线程之后,主线程继续执行而不会产生停滞等待的情况。

3. @Async介绍

在Spring中,基于@Async标注的方法,称之为异步方法;这些方法将在执行的时候,将会在独立的线程中被执行,调用者无需等待它的完成,即可继续其他的操作。

如何在Spring中启用@Async

基于Java配置的启用方式:

@Configuration
@EnableAsync
public class SpringAsyncConfig { ... }

基于XML配置文件的启用方式,配置如下:

<task:executor id="myexecutor" pool-size="5" />
<task:annotation-driven executor="myexecutor"/>

以上就是两种定义的方式。

4. 基于@Async无返回值调用

示例如下:

@Async //标注使用
public void asyncMethodWithVoidReturnType() {
  System.out.println("Execute method asynchronously. "
   + Thread.currentThread().getName());
}

使用的方式非常简单,一个标注即可解决所有的问题。

5. 基于@Async返回值的调用

示例如下:

@Async
public Future<String> asyncMethodWithReturnType() {
  System.out.println("Execute method asynchronously - "
   + Thread.currentThread().getName());
  try {
    Thread.sleep(5000);
    return new AsyncResult<String>("hello world !!!!");
  } catch (InterruptedException e) {
    //
  } 

  return null;
}

以上示例可以发现,返回的数据类型为Future类型,其为一个接口。具体的结果类型为AsyncResult,这个是需要注意的地方。

调用返回结果的异步方法示例:

public void testAsyncAnnotationForMethodsWithReturnType()
  throws InterruptedException, ExecutionException {
  System.out.println("Invoking an asynchronous method. "
   + Thread.currentThread().getName());
  Future<String> future = asyncAnnotationExample.asyncMethodWithReturnType(); 

  while (true) { ///这里使用了循环判断,等待获取结果信息
    if (future.isDone()) { //判断是否执行完毕
      System.out.println("Result from asynchronous process - " + future.get());
      break;
    }
    System.out.println("Continue doing something else. ");
    Thread.sleep(1000);
  }
}

分析: 这些获取异步方法的结果信息,是通过不停的检查Future的状态来获取当前的异步方法是否执行完毕来实现的。

6. 基于@Async调用中的异常处理机制

在异步方法中,如果出现异常,对于调用者caller而言,是无法感知的。如果确实需要进行异常处理,则按照如下方法来进行处理:

1.  自定义实现AsyncTaskExecutor的任务执行器

在这里定义处理具体异常的逻辑和方式。

2.  配置由自定义的TaskExecutor替代内置的任务执行器

示例步骤1,自定义的TaskExecutor

public class ExceptionHandlingAsyncTaskExecutor implements AsyncTaskExecutor {
  private AsyncTaskExecutor executor;
  public ExceptionHandlingAsyncTaskExecutor(AsyncTaskExecutor executor) {
    this.executor = executor;
   }
   ////用独立的线程来包装,@Async其本质就是如此
  public void execute(Runnable task) {
   executor.execute(createWrappedRunnable(task));
  }
  public void execute(Runnable task, long startTimeout) {
    /用独立的线程来包装,@Async其本质就是如此
    executor.execute(createWrappedRunnable(task), startTimeout);
  }
  public Future submit(Runnable task) { return executor.submit(createWrappedRunnable(task));
    //用独立的线程来包装,@Async其本质就是如此。
  }
  public Future submit(final Callable task) {
   //用独立的线程来包装,@Async其本质就是如此。
    return executor.submit(createCallable(task));
  }  

  private Callable createCallable(final Callable task) {
    return new Callable() {
      public T call() throws Exception {
         try {
           return task.call();
         } catch (Exception ex) {
           handle(ex);
           throw ex;
          }
         }
    };
  } 

  private Runnable createWrappedRunnable(final Runnable task) {
     return new Runnable() {
       public void run() {
         try {
           task.run();
         } catch (Exception ex) {
           handle(ex);
          }
      }
    };
  }
  private void handle(Exception ex) {
   //具体的异常逻辑处理的地方
   System.err.println("Error during @Async execution: " + ex);
  }
}

分析: 可以发现其是实现了AsyncTaskExecutor, 用独立的线程来执行具体的每个方法操作。在createCallable和createWrapperRunnable中,定义了异常的处理方式和机制。

handle()就是未来我们需要关注的异常处理的地方。

配置文件中的内容:

<task:annotation-driven executor="exceptionHandlingTaskExecutor" scheduler="defaultTaskScheduler" />
<bean id="exceptionHandlingTaskExecutor" class="nl.jborsje.blog.examples.ExceptionHandlingAsyncTaskExecutor">
  <constructor-arg ref="defaultTaskExecutor" />
</bean>
<task:executor id="defaultTaskExecutor" pool-size="5" />
<task:scheduler id="defaultTaskScheduler" pool-size="1" />

分析: 这里的配置使用自定义的taskExecutor来替代缺省的TaskExecutor。

7. @Async调用中的事务处理机制

在@Async标注的方法,同时也适用了@Transactional进行了标注;在其调用数据库操作之时,将无法产生事务管理的控制,原因就在于其是基于异步处理的操作。

那该如何给这些操作添加事务管理呢?可以将需要事务管理操作的方法放置到异步方法内部,在内部被调用的方法上添加@Transactional.

例如:  方法A,使用了@Async/@Transactional来标注,但是无法产生事务控制的目的。

方法B,使用了@Async来标注,  B中调用了C、D,C/D分别使用@Transactional做了标注,则可实现事务控制的目的。

8. 总结

通过以上的描述,应该对@Async使用的方法和注意事项了。

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

(0)

相关推荐

  • SpringMVC实现注解式权限验证的实例

    对大部分系统来说都需要权限管理来决定不同用户可以看到哪些内容,那么如何在Spring MVC中实现权限验证呢?当然我们可以继续使用servlet中的过滤器Filter来实现.但借助于Spring MVC中的action拦截器我们可以实现注解式的权限验证. 一.首先介绍一下action拦截器: HandlerInterceptor是Spring MVC为我们提供的拦截器接口,来让我们实现自己的处理逻辑,HandlerInterceptor 的内容如下: public interface Handl

  • Spring Boot统一异常处理详解

    Spring Boot中默认带了error的映射,但是这个错误页面显示给用户并不是很友好. 统一异常处理 通过使用@ControllerAdvice定义统一异常处理的类,而不是在每个Controller中逐个定义. @ExceptionHandler用来定义函数针对的函数类型,最后将Exception对象和请求URL映射到URL中. @ControllerAdvice class ExceptionTranslator { public static final String DEFAULT_E

  • 深入理解Spring Boot的日志管理

    前言 Spring Boot在所有内部日志中使用Commons Logging,但是默认配置也提供了对常用日志的支持, 如:Java Util Logging,Log4J, Log4J2和Logback.每种Logger都可以通过配置使用控制台或者文件输出日志内容. 日志输出格式 2016-08-19 10:22:04.233 INFO 7368 --- [ main] com.juzi.AsyncTest : Started AsyncTest in 10.084 seconds (JVM r

  • 详解Spring中的JavaConfig注解

    前言 大家都知道传统spring一般都是基于xml配置的,不过后来新增了许多JavaConfig的注解.特别是springboot,基本都是清一色的java config,不了解一下,还真是不适应.这里备注一下. @RestController spring4为了更方便的支持restfull应用的开发,新增了RestController的注解,比Controller注解多的功能就是给底下的RequestMapping方法默认都加上ResponseBody注解,省得自己再去每个去添加该注解. @C

  • spring mvc中的@PathVariable获得请求url中的动态参数

    spring mvc中的@PathVariable是用来获得请求url中的动态参数的,十分方便,复习下: @Controller public class TestController { @RequestMapping(value="/user/{userId}/roles/{roleId}",method = RequestMethod.GET) public String getLogin(@PathVariable("userId") String user

  • Spring集成MyBatis框架

    Java在写数据库查询时,我接触过四种方式: 1.纯Java代码,引用对应的数据库驱动包,自己写连接与释放逻辑(可以用连接池) 这种模式实际上性能是非常不错的,但是使用起来并不是非常方便:一是要手工为Connection做获取与释放,大量的冗余代码也容易出错:另一个是,复杂的SQL用字符串写起来简直不可维护(换行.可视长度.参数都是问题). 2.使用Spring JdbcTemplate 这个其实还是挺不错的,配置比较简单,功能丰富上比手工管理Connection要舒服多了,而且代码也比较简洁.

  • SpringMVC实现controller中获取session的实例代码

    平时使用springMVC,在方法中访问session中经常很自然地调用Servlet API.用起来非常直观方便,一直没有多考虑什么. 比如这样: @RequestMapping(value = "/logout") public String logout(HttpSession session) { session.removeAttribute("user"); return "/login"; } 但毕竟这样对Servlet API产生

  • spring mvc 和ajax异步交互完整实例代码

    spring MVC 异步交互demo: 1.jsp页面: <%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR

  • 详解Http请求中Content-Type讲解以及在Spring MVC中的应用

    详解Http请求中Content-Type讲解以及在Spring MVC中的应用 引言: 在Http请求中,我们每天都在使用Content-type来指定不同格式的请求信息,但是却很少有人去全面了解content-type中允许的值有多少,这里将讲解Content-Type的可用值,以及在spring MVC中如何使用它们来映射请求信息. 1.  Content-Type MediaType,即是Internet Media Type,互联网媒体类型:也叫做MIME类型,在Http协议消息头中,

  • Spring中@Async用法详解及简单实例

    Spring中@Async用法 引言: 在Java应用中,绝大多数情况下都是通过同步的方式来实现交互处理的:但是在处理与第三方系统交互的时候,容易造成响应迟缓的情况,之前大部分都是使用多线程来完成此类任务,其实,在spring 3.x之后,就已经内置了@Async来完美解决这个问题,本文将完成介绍@Async的用法. 1.  何为异步调用? 在解释异步调用之前,我们先来看同步调用的定义:同步就是整个处理过程顺序执行,当各个过程都执行完毕,并返回结果. 异步调用则是只是发送了调用的指令,调用者无需

  • 微信小程序中input标签详解及简单实例

    微信小程序中input标签详解及简单实例 使用input标签,我们都会,在微信小程序中使用,必定也是可以一下子就会的,但是却有些常用的属性无法按照习惯去使用: 我就用我最常用的来做例子: 一个一个来解读: 首先,我是定义了他的id,这是我们最常用的,所以就配了一个id,毕竟不操作他,又为什么设成输入框呢, 第二,设置他的样式, 第三,设置他的输入类别,以上都是很简单的 第四.使用正则l:哎限定输入为纯数字.这点可能有点不理解,这是对他的keyup事件监听,将不是纯数字的list无视掉.注意,是对

  • C++ 中回调函数详解及简单实例

    C++ 中回调函数详解及简单实例 回调函数调用关系图 对于第一种情况,主程序可以从执行到回调函数,也可以不执行回调函数,关键看宿主函数是否调用了回调函数. 对于第二种情况,主程序可以从执行到回调函数,也可以不执行回调函数,关键看宿主函数是否调用了回调函数.主程序不知道宿主函数什么时候调用回调函数,因为调用函数启动了线程,这样,主程序就不必关心宿主函数什么时候调用回调函数了,主程序可以释放出来做自己的事了. 代码示例 #include <stdio.h> typedef void(*lpFunc

  • java this 用法详解及简单实例

    java this 用法详解 用类名定义一个变量的时候,定义的只是一个引用,外面可以通过这个引用来访问这个类里面的属性和方法. 那们类里面是够也应该有一个引用来访问自己的属性和方法纳? 呵呵,JAVA提供了一个很好的东西,就是 this 对象,它可以在类里面来引用这个类的属性和方法.先来个简单的例子: public class ThisDemo { String name="Mick"; public void print(String name){ System.out.printl

  • Java基础之extends用法详解及简单实例

     Java extends用法详解 概要: 理解继承是理解面向对象程序设计的关键.在Java中,通过关键字extends继承一个已有的类,被继承的类称为父类(超类,基类),新的类称为子类(派生类).在Java中不允许多继承. (1)继承 class Animal{ void eat(){ System.out.println("Animal eat"); } void sleep(){ System.out.println("Animal sleep"); } vo

  • java 中cookie的详解及简单实例

    java 中cookie的详解 Java对cookie的操作比较简单,主要介绍下建立cookie和读取cookie,以及如何设定cookie的生命周期和cookie的路径问题. 建立一个无生命周期的cookie,即随着浏览器的关闭即消失的cookie,代码如下 HttpServletRequest request HttpServletResponse response Cookie cookie = new Cookie("cookiename","cookievalue&

  • C++中 STL list详解及简单实例

    C++中 STL list详解 1.List: 内部实现是一个双向链表,可以高效的进行插入删除,但不能够进行随机访问 2..示例程序: #include "stdafx.h" #include <iostream> #include <list> #include <iterator> #include <algorithm> using namespace std; const int num[5] = {1,3,2,4,5}; boo

  • Android Button的基本用法详解及简单实例

    Android Button的基本用法详解 Button的Click点击事件处理: mButtonBack.setOnClickListener(new OnClickListener() { @Override public void onClick(View arg0) { Log.e("================", "MenuBackBtnListen"); MenuimageView.setVisibility(View.INVISIBLE); mB

  • Java中的泛型方法详解及简单实例

      java 泛型方法: 泛型是什么意思在这就不多说了,而Java中泛型类的定义也比较简单,例如:public class Test<T>{}.这样就定义了一个泛型类Test,在实例化该类时,必须指明泛型T的具体类型,例如:Test<Object> t = new Test<Object>();,指明泛型T的类型为Object. 但是Java中的泛型方法就比较复杂了. 泛型类,是在实例化类的时候指明泛型的具体类型:泛型方法,是在调用方法的时候指明泛型的具体类型.   定

  • 微信小程序中的onLoad详解及简单实例

    微信小程序中的onLoad onLoad是一个生命周期函数,表示页面加载 onLoad默认有一个Object类型的参数,是指其他页面打开当前页面所调用的 query 参数 举个栗子~ 当我们在页面first的js脚本中有一个点击方法onTap 当点击时页面跳转到second页面,用?id = secondId的形式为second页面传递一个值(这里的secondId是一个已经获取过的值) onTap: function(){ wx.navigateTo({ url: "second?id=&qu

随机推荐