Spring开启@Async异步(javaconfig配置)
在Spring中,基于@Async标注的方法,称之为异步方法;这些方法将在执行的时候,将会在独立的线程中被执行,调用者无需等待它的完成,即可继续其他的操作。
应用场景
- 某些耗时较长的而用户不需要等待该方法的处理结果
- 某些耗时较长的方法,后面的程序不需要用到这个方法的处理结果时
代码
创建AsyncTask
1
2
3
4
5
6
7
8
9
10
11
12
13
|
/** * 异步任务 * * @author Peng */ public class AsyncTask { @Async public void doAsyncTask() throws InterruptedException { // 假设执行一个很耗时的任务 Thread.sleep( 10 * 1000 ); System.out.println( "执行完成,我执行了10秒" ); } } |
创建spring配置AppConfig
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
/** * spring 配置 * * @author Peng */ @Configuration @EnableAsync public class AppConfig { /** * 声明异步任务bean * * @return */ @Bean public AsyncTask asyncTask() { return new AsyncTask(); } } |
测试
1
2
3
4
5
6
7
8
9
10
11
12
13
|
/** * 异步测试 * * @author Peng */ public class AppTest { public static void main(String[] args) throws InterruptedException { ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig. class ); AsyncTask task = ctx.getBean(AsyncTask. class ); task.doAsyncTask(); System.out.println( "异步任务调用成功,返回客户端执行成功,异步任务继续执行" ); } } |
执行结果
异步任务调用成功,返回客户端执行成功,异步任务继续执行
执行完成,我执行了10秒
从结果可以看出,异步任务测试成功!
Spring @Async Demo
模拟一个业务场景:系统新用户注册成功后,异步发送邮件。
Project Directory
Maven Dependency
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
|
<? xml version = "1.0" encoding = "UTF-8" ?> < project xmlns = "http://maven.apache.org/POM/4.0.0" xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation = "http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd" > < modelVersion >4.0.0</ modelVersion > < groupId >org.fool.springasync</ groupId > < artifactId >springasync</ artifactId > < name >springasync</ name > < packaging >war</ packaging > < version >1.0.0-BUILD-SNAPSHOT</ version > < properties > < org.springframework-version >4.2.8.RELEASE</ org.springframework-version > </ properties > < dependencies > <!-- Spring --> < dependency > < groupId >org.springframework</ groupId > < artifactId >spring-context</ artifactId > < version >${org.springframework-version}</ version > </ dependency > < dependency > < groupId >org.springframework</ groupId > < artifactId >spring-webmvc</ artifactId > < version >${org.springframework-version}</ version > </ dependency > < dependency > < groupId >com.fasterxml.jackson.core</ groupId > < artifactId >jackson-databind</ artifactId > < version >2.8.1</ version > </ dependency > < dependency > < groupId >com.google.guava</ groupId > < artifactId >guava</ artifactId > < version >19.0</ version > </ dependency > < dependency > < groupId >org.apache.commons</ groupId > < artifactId >commons-lang3</ artifactId > < version >3.4</ version > </ dependency > <!-- @Inject --> < dependency > < groupId >javax.inject</ groupId > < artifactId >javax.inject</ artifactId > < version >1</ version > </ dependency > <!-- Servlet --> < dependency > < groupId >javax.servlet</ groupId > < artifactId >javax.servlet-api</ artifactId > < version >3.1.0</ version > < scope >provided</ scope > </ dependency > < dependency > < groupId >javax.servlet.jsp</ groupId > < artifactId >javax.servlet.jsp-api</ artifactId > < version >2.3.1</ version > < scope >provided</ scope > </ dependency > < dependency > < groupId >javax.servlet</ groupId > < artifactId >jstl</ artifactId > < version >1.2</ version > </ dependency > <!-- Test --> < dependency > < groupId >junit</ groupId > < artifactId >junit</ artifactId > < version >4.12</ version > < scope >test</ scope > </ dependency > </ dependencies > < build > < plugins > < plugin > < groupId >org.apache.maven.plugins</ groupId > < artifactId >maven-eclipse-plugin</ artifactId > < version >2.10</ version > < configuration > < additionalProjectnatures > < projectnature >org.springframework.ide.eclipse.core.springnature</ projectnature > </ additionalProjectnatures > < additionalBuildcommands > < buildcommand >org.springframework.ide.eclipse.core.springbuilder</ buildcommand > </ additionalBuildcommands > < downloadSources >true</ downloadSources > < downloadJavadocs >true</ downloadJavadocs > </ configuration > </ plugin > < plugin > < groupId >org.apache.maven.plugins</ groupId > < artifactId >maven-compiler-plugin</ artifactId > < version >3.5.1</ version > < configuration > < source >1.8</ source > < target >1.8</ target > < compilerArgument >-Xlint:all</ compilerArgument > < showWarnings >true</ showWarnings > < showDeprecation >true</ showDeprecation > </ configuration > </ plugin > < plugin > < groupId >org.eclipse.jetty</ groupId > < artifactId >jetty-maven-plugin</ artifactId > < version >9.3.11.v20160721</ version > < configuration > < scanIntervalSeconds >10</ scanIntervalSeconds > < httpConnector > < port >8888</ port > </ httpConnector > < webApp > < contextPath >/springasync</ contextPath > </ webApp > </ configuration > </ plugin > </ plugins > </ build > </ project > |
web.xml
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
|
<? xml version = "1.0" encoding = "UTF-8" ?> < web-app xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xmlns = "http://java.sun.com/xml/ns/javaee" xmlns:web = "http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" xsi:schemaLocation = "http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id = "WebApp_ID" version = "3.0" > <!-- The definition of the Root Spring Container shared by all Servlets and Filters --> < context-param > < param-name >contextConfigLocation</ param-name > < param-value >/WEB-INF/spring/root-context.xml</ param-value > </ context-param > <!-- Creates the Spring Container shared by all Servlets and Filters --> < listener > < listener-class >org.springframework.web.context.ContextLoaderListener</ listener-class > </ listener > <!-- Processes application requests --> < servlet > < servlet-name >appServlet</ servlet-name > < servlet-class >org.springframework.web.servlet.DispatcherServlet</ servlet-class > < init-param > < param-name >contextConfigLocation</ param-name > < param-value >/WEB-INF/spring/appServlet/servlet-context.xml</ param-value > </ init-param > < load-on-startup >1</ load-on-startup > </ servlet > < servlet-mapping > < servlet-name >appServlet</ servlet-name > < url-pattern >/</ url-pattern > </ servlet-mapping > </ web-app > |
root-context.xml
1
2
3
4
5
6
7
|
<? xml version = "1.0" encoding = "UTF-8" ?> < beans xmlns = "http://www.springframework.org/schema/beans" xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation = "http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" > <!-- Root Context: defines shared resources visible to all other web components --> </ beans > |
servlet-context.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
<? xml version = "1.0" encoding = "UTF-8" ?> < beans:beans xmlns = "http://www.springframework.org/schema/mvc" xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xmlns:beans = "http://www.springframework.org/schema/beans" xmlns:context = "http://www.springframework.org/schema/context" xmlns:task = "http://www.springframework.org/schema/task" xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd"> <!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure --> <!-- Enables the Spring MVC @Controller programming model --> < annotation-driven /> <!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory --> < resources mapping = "/resources/**" location = "/resources/" /> <!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory --> < beans:bean class = "org.springframework.web.servlet.view.InternalResourceViewResolver" > < beans:property name = "prefix" value = "/WEB-INF/views/" /> < beans:property name = "suffix" value = ".jsp" /> </ beans:bean > < context:component-scan base-package = "org.fool.springasync" /> </ beans:beans > |
AsyncConfig.java
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
|
package org.fool.springasync; import java.util.concurrent.Executor; import java.util.concurrent.ThreadPoolExecutor; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; @Configuration @EnableAsync @PropertySource ( "classpath:async.properties" ) public class AsyncConfig { /** Set the ThreadPoolExecutor's core pool size. */ @Value ( "${core.pool.size}" ) private Integer corePoolSize; /** Set the ThreadPoolExecutor's maximum pool size. */ @Value ( "${max.pool.size}" ) private Integer maxPoolSize; /** Set the capacity for the ThreadPoolExecutor's BlockingQueue. */ @Value ( "${queue.capacity}" ) private Integer queueCapacity; @Value ( "${thread.name.prefix}" ) private String ThreadNamePrefix; @Bean public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfig() { return new PropertySourcesPlaceholderConfigurer(); } @Bean public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(corePoolSize); executor.setMaxPoolSize(maxPoolSize); executor.setQueueCapacity(queueCapacity); executor.setThreadNamePrefix(ThreadNamePrefix); // rejection-policy:当pool已经达到max size的时候,如何处理新任务 // CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行 executor.setRejectedExecutionHandler( new ThreadPoolExecutor.CallerRunsPolicy()); executor.initialize(); return executor; } } |
Note:
AsyncConfig使用Annotation进行Spring Async的配置,当然也可以用XML的方式进行配置,只需要在servlet-context.xml中添加task的命名空间,同时加下如下两行配置:
1
2
3
|
<!-- Enables Spring Async --> < task:annotation-driven executor = "asyncExecutor" /> < task:executor id = "asyncExecutor" pool-size = "2-4" queue-capacity = "10" /> |
<task:executor />配置参数:
id:当配置多个executor时,被@Async("id")指定使用;也被作为线程名的前缀。
pool-size:
- core size:最小的线程数,缺省:1
- max size:最大的线程数,缺省:Integer.MAX_VALUE
queue-capacity:当最小的线程数已经被占用满后,新的任务会被放进queue里面,当这个queue的capacity也被占满之后,pool里面会创建新线程处理这个任务,直到总线程数达到了max size,这时系统会拒绝这个任务并抛出TaskRejectedException异常(缺省配置的情况下,可以通过rejection-policy来决定如何处理这种情况)。缺省值为:Integer.MAX_VALUE
keep-alive:超过core size的那些线程,任务完成后,再经过这个时长(秒)会被结束掉
rejection-policy:当pool已经达到max size的时候,如何处理新任务
- ABORT(缺省):抛出TaskRejectedException异常,然后不执行
- DISCARD:不执行,也不抛出异常
- DISCARD_OLDEST:丢弃queue中最旧的那个任务
- CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
MailService.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
package org.fool.springasync; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; @Component public class MailService { @Async public void sendMail(String username) { System.out.println( "Send Mail initialization..." ); System.out.println( "Execute method asynchronously - " + Thread.currentThread().getName()); try { Thread.sleep( 5000 ); System.out.println( "Welcome " + username); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println( "Send Mail Async done!!!" ); } } |
User.java
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
|
package org.fool.springasync; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; public class User { private Long id; private String username; private String password; public User() { } public User(Long id, String username, String password) { this .id = id; this .username = username; this .password = password; } public Long getId() { return id; } public void setId(Long id) { this .id = id; } public String getUsername() { return username; } public void setUsername(String username) { this .username = username; } public String getPassword() { return password; } public void setPassword(String password) { this .password = password; } @Override public String toString() { return ToStringBuilder.reflectionToString( this , ToStringStyle.SHORT_PREFIX_STYLE); } } |
UserService.java
1
2
3
4
5
6
7
8
9
10
11
12
13
|
package org.fool.springasync; import javax.inject.Inject; import org.springframework.stereotype.Service; @Service public class UserService { @Inject private MailService mailService; public void registerUser(User user) { System.out.println( "insert user to db..." ); mailService.sendMail(user.getUsername()); System.out.println( "register done, please check the email later!!!" ); } } |
UserController.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
package org.fool.springasync; import javax.inject.Inject; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; @Controller @RequestMapping ( "/user" ) public class UserController { @Inject private UserService userService; @RequestMapping (value = "/testasync" , method = RequestMethod.POST) @ResponseBody public User register( @RequestBody User user) { System.out.println(user); userService.registerUser(user); return user; } } |
Test
http://localhost:8888/springasync/user/testasync
POST请求(send两次)
Console Output
以上为个人经验,希望能给大家一个参考,也希望大家多多支持服务器之家。
原文链接:https://retry.blog.csdn.net/article/details/79014329