美文网首页spring相关程序员
Spring @Async异步调用,实现异步发送邮件

Spring @Async异步调用,实现异步发送邮件

作者: 阿菜的博客 | 来源:发表于2016-09-30 09:53 被阅读3775次

    Spring异步调用

    Spring异步调用

    异步调用应用场景

    1. 在请求一个Controller时,需要进行操作A、B、C,默认情况下,属于同步调用,三个操作依次进行。如果把B设置成一个异步方法,在执行完操作A之后,必用等待B执行完成,直接开始执行C,等到C执行完成后,整个方法就会返回,不管操作B是否已经执行完成。

    2. 以上描述有点干,来个实际的应用。大家在某东下单之后,会收到一封订单确认邮件。如果用同步调用的方法,当按下确认下单的时候,后台需要在处理完数据库,发完邮件之后,客户端页面才会跳转到下单成功页面,这会导致用户体验下降,甚至可能导致用户多次点击下单按钮。若使用异步调用,就可以做到读写数据库和发送邮件异步操作了。当然这里有个前提,发送邮件延迟一会是不会导致用户体验下降的。

    <br />

    代码

    那么就来看一下,在基于Spring MVC的项目中,异步调用是怎么实现的。
    首先,注意,异步调用在Spring 3.x之后使用比较方便,建议使用3.x版本。

    第一步:配置web.xml

    让在web.xml中配置的servelet和filter都支持异步调用。
    <async-supported>true</async-supported>

    <servlet>   
      <description>spring mvc servlet</description>   
      <servlet-name>springMvc</servlet-name>   
      <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>   
        <init-param>      
          <description>spring mvc 配置文件</description>      
          <param-name>contextConfigLocation</param-name>      
          <param-value>classpath:spring-mvc.xml</param-value>   
        </init-param>   
      <load-on-startup>1</load-on-startup>   
      <async-supported>true</async-supported>
    </servlet>
    

    第二步:异步调用注解支持

    在Spring配置中加上对异步调用注解的支持。

    <!-- 支持异步方法执行 -->
    <task:executor id="myexecutor" pool-size="5"  />
    <task:annotation-driven executor="myexecutor"/>
    

    第三步:创建异步调用类和方法

    这里模拟异步发邮件,所以就创建异步发送邮件类。

    1. 把邮件发送类注册成bean,加上@Service即可。
    2. 在该类上加上@EnableAsync,表示该类支持异步调用。
    3. 在对应异步方法上加上@Async注解。
    @Service
    @EnableAsync
    public class SendMailUtil {
    
        private final static String SEND_HOST_NAME = "smtp.163.com";
        private final static String SEND_FROM = "-----------";
        private final static String SEND_AUTH_ACCOUNT = "-----------";
        private final static String SEND_AUTH_PASSWORD = "-----------";
    
        @Async
        public void SEND_SUBMIT_EMAIL(String sendToAccount, String sendToName) {
            //为了体现效果,邮件发送延迟5秒钟
            try {
                Thread.sleep(5 * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Mail is sent!");
            //使用commons-mail发送邮件
            SimpleEmail email = new SimpleEmail();
            try {
                email.setHostName(SEND_HOST_NAME);
                email.addTo(sendToAccount, sendToName);
                email.setFrom(SEND_AUTH_ACCOUNT, SEND_FROM);
                email.setAuthentication(SEND_AUTH_ACCOUNT, SEND_AUTH_PASSWORD);
                email.setSubject("This is a @Async eamil!");
                email.setMsg("This is a simple test of commons-email");
                //需要对发送错误情况做处理
                email.send();
            } catch (EmailException e) {
                // 这里需要对邮件发送做异常处理
                e.printStackTrace();
            }
        }
    }
    

    第四步:在Controller中调用异步方法即可

    @Controller
    @RequestMapping("/asyncController")
    public class AsyncController extends BaseController {
    
        //注入邮件类
        @Autowired
        private SendMailUtil sendMailUtil;
    
        //注入其他服务类
        @Autowired
        private OtherService otherService;
    
        @RequestMapping(params = "async")
        @ResponseBody
        public Json async(HttpServletRequest request) {
            Json j = new Json();
            Other other = OtherService.other();
            if (other != null) {
                j.setObj(other);
                j.setSuccess(true);
                //异步发送邮件
                sendMailUtil.SEND_SUBMIT_EMAIL("-----------", "-----------");
                System.out.println("Execute before sending mail!");
                //在邮件发送之前就返回了
                return j;
            }
        }
    }
    

    <br />

    错误处理

    1. 如果邮件发送失败了该怎么处理?很多网站都会在用户注册的时候发送激活邮件,如果一段时间没收到,网站会提供一个再次发送邮件的按钮。
    2. 类似的,如果做其他异步请求,需要考虑请求失败的情况。

    <br />

    异步调用实现原理

    Spring会为异步调用创建一个线程。
    在没有异步调用之前,可以手工创建一个线程来实现。

    [转]Spring 在扫描bean的时候会扫描方法上是否包含@async的注解,如果包含的,spring会为这个bean动态的生成一个子类(那应该是用cglib代理),我们称之为代理类,代理类是继承我们所写的bean的,然后把代理类注入进来。

    那此时,在执行此方法的时候,会到代理类中,代理类判断了此方法需要异步执行,就不会调用父类(我们原本写的bean)的对应方法。

    Spring自己维护了一个队列,他会把需要执行的方法,放入队列中,等待线程池去读取这个队列,完成方法的执行,从而完成了异步的功能。

    我们可以关注到再配置task的时候,是有参数让我们配置线程池的数量的。

    相关文章

      网友评论

      • jeffDeng:我想问的问题是
        //异步发送邮件
        sendMailUtil.SEND_SUBMIT_EMAIL("-----------", "-----------");

        这里处理完之后,怎样得到结果,通知客户端?

      本文标题:Spring @Async异步调用,实现异步发送邮件

      本文链接:https://www.haomeiwen.com/subject/vrmlyttx.html