版权声明:“假装、是个有灵魂的程序员” —— Go Big Or Go Home https://blog.csdn.net/u011663149/article/details/88561868
前言:
任何与业务逻辑没有直接关联的逻辑(横切关注点)或在调用者上下文中不需要响应以确定下一个流或任何业务的逻辑是Asyncronization的理想候选者。在Spring中使用@Async注释使用异步,如果你在方法上使用随机的@Async注释并且认为你的方法将在一个单独的线程中以异步方式调用这是错的。
要知道@Async如何工作的和它的特性,不然你就无法理解异步行为。
@Async 如何工作?
当在方法上添加@Async注释时,它会基于proxyTargetClass 属性创建该对象的代理,其中定义了@Async(JDK Proxy/CGlib) 。Spring 尝试查找与上下文关联的线程池,将此方法的逻辑作为单独的执行路径提交,如果未找到则会搜索唯一的 TaskExecutor bean或名为taskExecutor的bean使用默认的 SimpleAsyncTaskExecutor。
@Async的限制
1.编写一个类并创建方法,将@Async置于该方法之上,如果通过创建本地实例从另一个类中使用该类,那么它将不会触发异步,它必须由Spring @ComponentScan注释扫描或在标记为@Configuration的类中创建。
//被调用方法
import java.util.Map;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
@Component
public class AsyncMailTrigger {
@Async
public void senMail(Map<String,String> properties) {
System.out.println("在新线程中发送邮件 :: " + Thread.currentThread().getName());
properties.forEach((K,V)->System.out.println("Key::" + K + " Value ::" + V));
}
}
//调用
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class AsyncCaller {
@Autowired
AsyncMailTrigger asyncMailTriggerObject;
public void rightWayToCall() {
System.out.println("从RightWayToCall线程调用 " + Thread.currentThread().getName());
asyncMailTriggerObject.senMail(populateMap());
}
public void wrongWayToCall() {
System.out.println("从错误的waytocall线程调用 " + Thread.currentThread().getName());
AsyncMailTrigger asyncMailTriggerObject = new AsyncMailTrigger();
asyncMailTriggerObject.senMail(populateMap());
}
private Map<String, String> populateMap() {
Map<String, String> mailMap = new HashMap<String, String>();
mailMap.put("body", "一个内容");
return mailMap;
}
}
运行结果:
从RightWayToCall线程调用
2019-03-09 14:08:28.893 INFO 8468 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
在新线程中触发邮件 :: task-1
Key::body Value ::一个内容
++++++++++++++++
从错误的wayToCall线程主调用
在新线程中触发邮件 :: main
Key::body Value ::一个内容
注意:
1、不要在私有方法之上使用@Async,因为在运行时它将无法创建proxy。
@Async
private void senMail() {
System.out.println("A proxy on Private method " + Thread.currentThread().getName());
}
2、不要在调用方法相同的Async方法的同一个类中编写Async方法,因为虽然它创建了一个代理但是此调用绕过代理并直接调用该方法,Caller方法应该在不同的类中,同时调用异步方法。
扫描二维码关注公众号,回复:
5650992 查看本文章
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
@Component
public class AsyncCaller {
@Autowired
AsyncMailTrigger asyncMailTriggerObject;
public void rightWayToCall() {
System.out.println("从RightWayToCall线程调用 " + Thread.currentThread().getName());
asyncMailTriggerObject.senMail(populateMap());
}
public void wrongWayToCall() {
System.out.println("从 wrongWayToCall线程调用 " + Thread.currentThread().getName());
this.senMail(populateMap());
}
private Map<String, String> populateMap() {
Map<String, String> mailMap = new HashMap<String, String>();
mailMap.put("body", "一个内容");
return mailMap;
}
@Async
public void senMail(Map<String, String> properties) {
System.out.println("在新线程中触发邮件 :: " + Thread.currentThread().getName());
properties.forEach((K, V) -> System.out.println("Key::" + K + " Value ::" + V));
}
}
Application:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableAsync;
import com.example.ask2shamik.springAsync.demo.AsyncCaller;
@SpringBootApplication
@EnableAsync
public class DemoApplication {
@Autowired
AsyncCaller caller;
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Bean
public CommandLineRunner commandLineRunner(ApplicationContext ctx) {
return args -> {
caller.rightWayToCall();
Thread.sleep(1000);
System.out.println("++++++++++++++++");
Thread.sleep(1000);
caller.wrongWayToCall();
};
}
}
结论:理解异步如何在内部工作及其限制,后续补充Exception in Async.