使用Spring开启@Async异步方式(javaconfig配置)

时间:2022-07-02 15:28:57

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

使用Spring开启@Async异步方式(javaconfig配置)

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两次)

使用Spring开启@Async异步方式(javaconfig配置)

Console Output

使用Spring开启@Async异步方式(javaconfig配置)

以上为个人经验,希望能给大家一个参考,也希望大家多多支持服务器之家。

原文链接:https://retry.blog.csdn.net/article/details/79014329