• 前言

    最近在做一个用户反馈的功能,就是当用户反馈意见或建议后服务端将意见保存然后发邮件给相关模块的开发者。考虑发邮件耗时的情况所以我想用异步的方法去执行,于是就开始研究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. sql server实现简繁转换

    /*--调用示例 gb_to_big和big_to_gb表存放着常用的简繁字 --可以百度到常用的简体汉字,然后用excel转换成繁体  再导入数据库. --转换为繁体 select dbo.f_GB ...

  2. 项目管理目标:添加人员并向其分配任务 - Project

    已剪辑自: https://support.office.com/zh-cn/article/%E9%A1%B9%E7%9B%AE%E7%AE%A1%E7%90%86%E7%9B%AE%E6%A0%8 ...

  3. Excel—错误解释

       1.#####! 如果单元格所含的数字.日期或时间比单元格宽,或者单元格的日期时间公式产生了一个负值,就会产生#####!.这个看起来比较简单,大家应该都了解吧. 解决方法:如果单元格所含的数字 ...

  4. C++ 初读迭代器

    迭代器 这是个啥? string对象或vector对象可以通过下标访问每一个元素,迭代器也具有同样的效果.那又有什么不同呢?事实上并不是所有的容器到可以使用下标访问每一个元素,即在容器上迭代器更具普适 ...

  5. 微信小程序,转盘抽奖

    微信小程序大转盘 代码源码:https://github.com/yewook/Lottery-turntable

  6. Springmvc 横向源码原理解析(原创)

    1.springmvc的基本流程(不多赘述) 2.主要涉及到的类 //该方法返回HandlerExecutionChain 类 并不是直接返回handler 是因为在HandlerExecutionC ...

  7. iOS 获取app进程被杀死事件

    程序被用户双击上滑杀死后,就对app做一些特殊的处理 下面的方法可以获取到用户双击上滑杀死的事件 - (void)applicationDidEnterBackground:(UIApplicatio ...

  8. mysql5.7.X版本only_full_group_by问题解决

    一.出错原因 最近因为开发数据库与部署数据库版本不同,带来了几个问题,其中only_full_group_by问题是之前没有遇到的. 具体报错如下 [Err] 1055 - Expression #1 ...

  9. leetcode-只出现一次的数字

    题目:只出现一次的数字 给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次.找出那个只出现了一次的元素. 说明: 你的算法应该具有线性时间复杂度. 你可以不使用额外空间来实现吗? ...

  10. DRC错误解决办法

    一.WARNING(ORCAP-1589): Net has two or more aliases - possible short? 错误原因:一个网络有两个网络标号,可能造成短路! 问题本质:原 ...