• 前言

    最近在做一个用户反馈的功能,就是当用户反馈意见或建议后服务端将意见保存然后发邮件给相关模块的开发者。考虑发邮件耗时的情况所以我想用异步的方法去执行,于是就开始研究Spring的@Async了。但网上的许多教程都是相互抄袭且直接复制代码的,根本没有去自己实践或者更改代码,所以在这两天我遇到了许多问题,使得@Async无效,也一直没有找到很好的文章去详细的说明@Async的正确及错误的使用方法及需要注意的地方,这里Sring是以配置文件的形式来开启@Async,而SpringBoot则是以注解的方式开启。
  • 教程
  1. Spring配置文件

    配置文件的话有两种方式一种是精简式:
    直接在applicationContext.xml中加入开启异步并添加task的命名空间
    <task:executor id="WhifExecutor" pool-size="10"/>
    <task:annotation-driven executor="WhifExecutor" />
    xmlns:task="http://www.springframework.org/schema/task"
    http://www.springframework.org/schema/task
    http://www.springframework.org/schema/task/spring-task.xsd

    这样好处是简单快速,缺点是线程池配置简单,线程创建不能定时关闭。

    所以我推荐下面这种,定义一个线程池,然后引入。其实两种方法原理是一样只不过下面这种只是线程池配置更加详细,线程在空闲后会根据存活时间配置进行关闭。

    在applicationContext.xml同目录下创建文件threadPool.xml内容如下,然后在applicationContext.xml中引入threadPool.xml:<import resource="threadPool.xml" />

    <?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:task="http://www.springframework.org/schema/task"
    	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                            http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd">
    
    	<!-- 开启异步,并引入线程池 -->
    	<task:annotation-driven executor="threadPool" />
    
    	<!-- 定义线程池 -->
    	<bean id="threadPool"
    		class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
    		<!-- 核心线程数,默认为1 -->
    		<property name="corePoolSize" value="5" />
    
    		<!-- 最大线程数,默认为Integer.MAX_VALUE -->
    		<property name="maxPoolSize" value="20" />
    
    		<!-- 队列最大长度,一般需要设置值>=notifyScheduledMainExecutor.maxNum;默认为Integer.MAX_VALUE -->
    		<property name="queueCapacity" value="500" />
    
    		<!-- 线程池维护线程所允许的空闲时间,默认为60s -->
    		<property name="keepAliveSeconds" value="30" />
    
    		<!-- 完成任务自动关闭 , 默认为false-->
    		<property name="waitForTasksToCompleteOnShutdown" value="true" />
    
    		<!-- 核心线程超时退出,默认为false -->
    		<property name="allowCoreThreadTimeOut" value="true" />
    
    		<!-- 线程池对拒绝任务(无线程可用)的处理策略,目前只支持AbortPolicy、CallerRunsPolicy;默认为后者 -->
    		<property name="rejectedExecutionHandler">
    			<!-- AbortPolicy:直接抛出java.util.concurrent.RejectedExecutionException异常 -->
    			<!-- CallerRunsPolicy:主线程直接执行该任务,执行完之后尝试添加下一个任务到线程池中,可以有效降低向线程池内添加任务的速度 -->
    			<!-- DiscardOldestPolicy:抛弃旧的任务、暂不支持;会导致被丢弃的任务无法再次被执行 -->
    			<!-- DiscardPolicy:抛弃当前任务、暂不支持;会导致被丢弃的任务无法再次被执行 -->
    			<bean class="java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy" />
    		</property>
    	</bean>
    </beans>
  2. SpringBoot则是添加@EnableAsync注解
    @EnableAsync
    @SpringBootApplication
    @ServletComponentScan
    @MapperScan("com.cmc.schedule.model.mapper") //配置扫描mapper接口的地址
    public class NLPApplication extends SpringBootServletInitializer {
    
    	//不使用springboot内嵌tomcat启动方式
    	@Override
    	protected SpringApplicationBuilder configure(
    			SpringApplicationBuilder application) {
    		return application.sources(NLPApplication.class);
    	}
    
    	public static void main(String[] args) {
    		SpringApplication.run(NLPApplication.class, args);
    	}
    
    	//默认使用fastjson解析
    	@Bean
    	public HttpMessageConverters fastJsonHttpMessageConverters() {
    		FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
    		FastJsonConfig fastJsonConfig = new FastJsonConfig();
    		fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);
    		fastConverter.setFastJsonConfig(fastJsonConfig);
    		HttpMessageConverter<?> converter = fastConverter;
    		return new HttpMessageConverters(converter);
    	}
    }
  3. 这一步Spring和SpringBoot都是一样的,创建一个异步方法的类
    package com.cmc.tst;
    
    import org.springframework.scheduling.annotation.Async;
    import org.springframework.stereotype.Component;
    
    /**
     * @Component 注解必须要有,否则无法将此bean注入
     * 当然也可以使用其他的注解,只要可以装配就行
     *
     * @author chenmc
     * @date 2017年9月4日 下午3:38:29
     */
    @Component
    public class MyAsync {
    
    	/**
    	 * @Async 表明这是一个异步方法,也就是说当调用这个方法时,
    	 * spring会创建一条线程来执行这个方法。
    	 * 注意:不能使用static来修饰此方法,否则@Async无效
    	 *
    	 * @author chenmc
    	 * @date 2017年9月4日 下午3:34:24
    	 */
    	@Async
    	public void asyncMethod(){
    		System.out.println(Thread.currentThread().getName());
    	}
    }
  4. 测试调用异步方法,我这里使用的是JUnit4测试
    	@Autowired
    	MyAsync async;
    
    	@Test
    	public void test() {
    		System.out.println(Thread.currentThread().getName() + "start");
    		//MyAsync async = new MyAsync(); //自己new出来的对象@Async将无效,必须要spring注入的
    		async.asyncMethod();
    		try {
    			Thread.sleep(500);
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    		System.out.println(Thread.currentThread().getName() + "end");
    	}
  5. 总结

    这里总结一下@Async注解无效的可能点

    一、异步方法使用static修饰

    二、异步类没有使用@Component注解(或其他注解)导致spring无法扫描到异步类

    三、测试异步方法不能与异步方法在同一个类中

    四、测试类中需要使用@Autowired或@Resource等注解自动注入,不能自己手动new对象

    五、如果使用SpringBoot框架必须在启动类中增加@EnableAsync注解
  • 结语

    spring的@Async真的极大的方便了java的异步(多线程)开发,心里默念三遍:感谢spring!感谢spring!感谢spring!

Spring及SpringBoot @Async配置步骤及注意事项的更多相关文章

  1. SpringBoot线程池的创建、@Async配置步骤及注意事项

    最近在做订单模块,用户购买服务类产品之后,需要进行预约,预约成功之后分别给商家和用户发送提醒短信.考虑发短信耗时的情况所以我想用异步的方法去执行,于是就在网上看见了Spring的@Async了. 但是 ...

  2. IntelliJ IDEA 2017 MySQL5 绿色版 Spring 4 Mybatis 3 配置步骤详解(二)

    前言    继续上一篇安装教程 首先是MySQL绿色版安装之后其他组件安装,如果篇幅较长会分为多篇深入讲解,随笔属于学习笔记诸多错误还望指出 共同学习. MySQL 5.7 绿色版   我本地安装的是 ...

  3. SpringBoot自定义配置步骤

    1. 在yml中填写自定义配置 ly: sms: accessKeyId: # 短信配置 accessKeySecret: signName: xx商城 # 签名名称 verifyCodeTempla ...

  4. 整合struts2+hibernate详细配置步骤及注意事项

    刚刚学完这两个框架,就迫不及待的做了一个例子,在整合两个框架的时候,也碰到了一些小问题,下面介绍一下配置的步骤: 1.创建一个自定义的struts2和hibernate的类库 因为之前写例子都是直接将 ...

  5. spring-boot @Async 的使用、自定义Executor的配置方法

    1. TaskExecutor Spring异步线程池的接口类,其实质是java.util.concurrent.Executor Spring 已经实现的异常线程池: 1. SimpleAsyncT ...

  6. spring boot使用自定义配置的线程池执行Async异步任务

    一.增加配置属性类 package com.chhliu.springboot.async.configuration; import org.springframework.boot.context ...

  7. SpringBoot 自动配置:Spring Data JPA

    前言 不知道从啥时候开始项目上就一直用MyBatis,其实我个人更新JPA些,因为JPA看起来OO的思想更强烈些,所以这才最近把JPA拿出来再看一看,使用起来也很简单,除了定义Entity实体外,声明 ...

  8. spring基于xml的声明式事务控制配置步骤

    <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.sp ...

  9. SpringBoot 优雅配置跨域多种方式及Spring Security跨域访问配置的坑

    前言 最近在做项目的时候,基于前后端分离的权限管理系统,后台使用 Spring Security 作为权限控制管理, 然后在前端接口访问时候涉及到跨域,但我怎么配置跨域也没有生效,这里有一个坑,在使用 ...

随机推荐

  1. Linux-day1-pdf课件

    1.掌握Linux目录结构  2.掌握基础文件操作指令 3.vim常用操作命令 4.Linux用户和组

  2. 在WINDOWS中安装使用GSL(MinGW64+Sublime Text3 & Visual Studio)

    本文介绍在Windows下安装使用GSL库,涉及GSL两个版本(官方最新版及GSL1.8 VC版).msys shell.GCC.G++等内容,最终实现对GSL安装及示例基于MinGW64在Subli ...

  3. 学习easyui的小伙伴有福利了

    easy-ui常用属性和方法 css定义与js定义两个版本

  4. 练习2-1 Programming in C is fun!

    练习2-1 Programming in C is fun! 一 问题描述 本题要求编写程序,输出一个短句“Programming in C is fun!”. 输入格式: 本题目没有输入. 输出格式 ...

  5. 阿里云服务器 yii2执行composer提示报错

    未解决 composer installLoading composer repositories with package informationUpdating dependencies (inc ...

  6. linux 做了raid后,硬盘坏了更换问题

    系统做完raid1后发现 raid盘坏了,硬盘都是热插拔的,更换后,需要简单配置一下才能自动进行镜像拷贝. 在pd mgmt 页面,选择新加入的硬盘,按F2,选择 make global HS选项 选 ...

  7. RF经验~~

    在用RF进行web自动化脚本编写时,经常会用到对日期控件进行操作.目前认为比较好用的方法是:直接对日期控件进行赋值. Assign Id To Element //*[@id="update ...

  8. c# 集合的长度为什么是可变的

    摘要: 写在前面:此随笔仅仅是作为个人学习总结,有不对的地方,请各位前辈指正O(∩_∩)O........ 一: 引入 在学习集合之前我们都学习过数组.可以知道数组的长度在声明的时候就已经被固定了,不 ...

  9. 201771010118 马昕璐 《面向对象程序设计(java)》第十三周学习总结

    第一部分:理论知识学习部分 事件处理基础 1.事件源(event source):能够产生事件的对象都可以成为事件源.一个事件源是一个能够注册监听器并向监听器发送事件对象的对象. 2.事件监听器(ev ...

  10. Java_重载与重写

    在java中,重载与重写都是多态的体现.重载(Overload)体现的是编译时多态,而重写(Override)体现了运行时多态. 重载(Overload): 定义:在一个类中,同名的方法如果有不同的参 ...