spring 配置异步要点 @Async

  1. 原理: spring 在扫描bean的时候会扫描方法上是否包含@async的注解,如果包含的,spring会为这个bean动态的生成一个子类,我们称之为代理类(?),代理类是继承我们所写的bean的,然后把代理类注入进来,那此时,在执行此方法的时候,会到代理类中,代理类判断了此方法需要异步执行,就不会调用父类(我们原本写的bean)的对应方法。spring自己维护了一个队列,他会把需要执行的方法,放入队列中,等待线程池去读取这个队列,完成方法的执行,从而完成了异步的功能。


1.在spring.xml配置  <task:annotation-driven /> 

2.调用@Async

  1. public class JobUtilsTest{  
  2.       
  3.     @Autowired  
  4.     private DaoService service;  
  5.   
  6.     @Test  
  7.     public void testAsync() throws Exception {  
  8.         System.out.println("start" );  
  9.         service.update(); // ★ 假设这个方法会比较耗时,需要异步执行  
  10.         System.out.println("end");  
  11.           
  12.           
  13.         Thread.sleep(3000); // 因为junit结束会结束jvm,所以让它等会异步线程  
  14.     }  
  15. }
  16.   
    1. @Service  
    2. public class DaoService {  
    3.     @Async  
    4.     public void update() {  
    5.         try {  
    6.             Thread.sleep(2000);  
    7.             // do something  
    8.         } catch (InterruptedException e) {  
    9.             e.printStackTrace();  
    10.         }  
    11.         System.out.println("operation complete.");  
    12.     }  
    13. }  


    14. 输出结果:
    15.  

      start
      end
      operation complete.

       

      可以看出,输出不是顺序执行,说明异步调用成功了。


    16. web项目中的配置

      在spring mvc3.2及以上版本增加了对请求的异步处理,是在servlet3的基础上进行封装的。

      1、修改web.xml

      复制代码
      <?xml version="1.0" encoding="UTF-8"?>
      <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
          http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
      ...
      </web-app>
      复制代码

      1.1、声明version="3.0",声明web-app_3_0.xsd

      1.2、为servlet或者filter设置启用异步支持:<async-supported>true</async-supported>,修改WEB应用的web.xml

      复制代码
      <!-- spring mvc -->
      <servlet>
      <servlet-name>SpringMvc</servlet-name>
      <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
      <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>...</param-value>
      </init-param>
      <load-on-startup>1</load-on-startup>
      <async-supported>true</async-supported>
      </servlet>
      复制代码

       

      2、使controller类支持async

      2.1、返回java.util.concurrent.Callable来完成异步处理

      复制代码
      package org.springframework.samples.mvc.async;
        
      import java.util.concurrent.Callable;
        
      import org.springframework.stereotype.Controller;
      import org.springframework.ui.Model;
      import org.springframework.web.bind.annotation.ExceptionHandler;
      import org.springframework.web.bind.annotation.RequestMapping;
      import org.springframework.web.bind.annotation.RequestParam;
      import org.springframework.web.bind.annotation.ResponseBody;
      import org.springframework.web.context.request.async.WebAsyncTask;
        
      @Controller
      @RequestMapping("/async/callable")
      public class CallableController {
          @RequestMapping("/response-body")
          public @ResponseBody Callable<String> callable() {
        
              return new Callable<String>() {
                  @Override
                  public String call() throws Exception {
                      Thread.sleep(2000);
                      return "Callable result";
                  }
              };
          }
        
          @RequestMapping("/view")
          public Callable<String> callableWithView(final Model model) {
        
              return new Callable<String>() {
                  @Override
                  public String call() throws Exception {
                      Thread.sleep(2000);
                      model.addAttribute("foo", "bar");
                      model.addAttribute("fruit", "apple");
                      return "views/html";
                  }
              };
          }
        
          @RequestMapping("/exception")
          public @ResponseBody Callable<String> callableWithException(
                  final @RequestParam(required=false, defaultValue="true") boolean handled) {
        
              return new Callable<String>() {
                  @Override
                  public String call() throws Exception {
                      Thread.sleep(2000);
                      if (handled) {
                          // see handleException method further below
                          throw new IllegalStateException("Callable error");
                      }
                      else {
                          throw new IllegalArgumentException("Callable error");
                      }
                  }
              };
          }
        
          @RequestMapping("/custom-timeout-handling")
          public @ResponseBody WebAsyncTask<String> callableWithCustomTimeoutHandling() {
        
              Callable<String> callable = new Callable<String>() {
                  @Override
                  public String call() throws Exception {
                      Thread.sleep(2000);
                      return "Callable result";
                  }
              };
        
              return new WebAsyncTask<String>(1000, callable);
          }
        
          @ExceptionHandler
          @ResponseBody
          public String handleException(IllegalStateException ex) {
              return "Handled exception: " + ex.getMessage();
          }
        
      }
      复制代码

      2.2、在异步处理完成时返回org.springframework.web.context.request.async.DeferredResult其他线程,例如一个JMS或一个AMQP消息,Redis通知等等:

      复制代码
      @RequestMapping("/quotes")
      @ResponseBody
      public DeferredResult<String> quotes() {
        DeferredResult<String> deferredResult = new DeferredResult<String>();
        // Add deferredResult to a Queue or a Map...
        return deferredResult;
      }
          
      // In some other thread...
      deferredResult.setResult(data);
      // Remove deferredResult from the Queue or Map
      复制代码

       

      3、spring配置文件的修改

      spring mvc的dtd的声明必须大于等于3.2

       

      <mvc:annotation-driven>
      <!--  可不设置,使用默认的超时时间 -->
          <mvc:async-support default-timeout="3000"/>
      </mvc:annotation-driven>

       

      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);  
          }  
      }  
           
           
      • 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
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 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
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50

      分析: 可以发现其是实现了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" />  
           
           
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6

      分析: 这里的配置使用自定义的taskExecutor来替代缺省的TaskExecutor。 
      7. @Async调用中的事务处理机制

      在@Async标注的方法,同时也适用了@Transactional进行了标注;在其调用数据库操作之时,将无法产生事务管理的控制,原因就在于其是基于异步处理的操作。
      
       那该如何给这些操作添加事务管理呢?可以将需要事务管理操作的方法放置到异步方法内部,在内部被调用的方法上添加@Transactional.
      
      例如:  方法A,使用了@Async/@Transactional来标注,但是无法产生事务控制的目的。
      
            方法B,使用了@Async来标注,  B中调用了C、D,C/D分别使用@Transactional做了标注,则可实现事务控制的目的。
      
      1. 总结 
        通过以上的描述,应该对@Async使用的方法和注意事项了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值