eg業務場景: 用戶下單,訂單建立成功,須要發送郵件通知用戶,爲用戶的訂單建立行爲增長積分,短信通知等等(訂單主體,訂單商品附屬表信息,訂單發貨信息,訂單分期支付信息,訂單優惠信息,支付優惠信息)一系列的動做的處理。java
(發送郵件,建立訂單商品附屬表信息,訂單發貨信息,訂單分期支付信息,訂單優惠信息,支付優惠信息 等等)能夠理解爲事件;在關注的業務 如 【訂單的建立(事件源)】,建立訂單後須要【發送郵件通知(事件發佈)】(事件:須要發送郵件或是其餘業務) , 這些後續不影響訂單主流程的業務,能夠拆解到監聽業務處理。spring
事件發送後,須要通知的對象,告訴須要進行的下一步的操做。如 發郵件 。監聽到事件發送後,作具體的業務處理,調用發送郵件邏輯進行發送。異步
配置方式有兩種: 註解 和 xml配置async
<!--異步線程池能夠定義多個,若在使用註解 @Async 時沒有指定使用哪一個線程池,則使用默認的線程 1. ‘id' : 線程的名稱的前綴 2. ‘pool-size':線程池的大小。支持範圍」min-max」和固定值(此時線程池core和max sizes相同) 3. ‘queue-capacity' :排隊隊列長度 --> <!-- 缺省的異步任務線程池 --> <task:annotation-driven executor="asyncExecutor" /> <task:executor id="asyncExecutor" pool-size="100-10000" queue-capacity="10" /> <!-- 處理email發送的線程池 --> <task:executor id="emailExecutor" pool-size="15-1000" queue-capacity="5" keep-alive="5"/>
@Configuration @EnableAsync public class SpringConfig { /** Set the ThreadPoolExecutor's core pool size. */ private int corePoolSize = 10; /** Set the ThreadPoolExecutor's maximum pool size. */ private int maxPoolSize = 200; /** Set the capacity for the ThreadPoolExecutor's BlockingQueue. */ private int queueCapacity = 10; private String ThreadNamePrefix = "asyncExecutor-"; @Bean public Executor asyncExecutor() { 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; } }
這個註解用於標註某個方法或某個類裏面的全部方法都是須要異步處理的。被註解的方法被調用的時候,會在新線程中執行,而調用它的方法會在原來的線程中執行。這樣能夠避免阻塞、以及保證任務的實時性。適用於處理log、發送郵件、短信……等。ide
/** * 異步建立兌換碼 * @Title: TicektCodeListener * @Description: */ @Component public class TicektCodeListener { @Autowired private MkDiscountCardService mkDiscountCardService; //異步監聽器 @Async @EventListener public void dualEven(TicketCodeEvent event){ mkDiscountCardService.createCardCode(event.getDto()); } }
下面使用 一個優惠券的券碼建立業務,來舉例使用。Spring的監聽/觀察者 模式。this
步驟:.net
事件定義,用於監聽者獲取 事件的屬性,信息傳遞線程
/** * 兌換碼 * @Title: TODO */ public class TicketCodeEvent { private static final long serialVersionUID = 1L; private MkDiscountCardDto dto; public TicketCodeEvent(String code,MkDiscountCardDto dto) { this.code = code; this.dto = dto; } private String code; public String getCode() { return code; } public void setCode(String code) { this.code = code; } public MkDiscountCardDto getDto() { return dto; } public void setDto(MkDiscountCardDto dto) { this.dto = dto; } }
注意:code
/** * 異步建立兌換碼 * @Title: TicektCodeListener * @Description: */ @Component public class TicektCodeListener { @Autowired private MkDiscountCardService mkDiscountCardService; //異步監聽器 @Async @EventListener public void dualEven(TicketCodeEvent event){ mkDiscountCardService.createCardCode(event.getDto()); } }
使用註解注入獲取上下文,這次沒有采用 接口實現的方式xml
注意點:調用上下文 context.publishEvent(Event e) 發佈事件
@Service public class MkDiscountCardServiceImpl implements MkDiscountCardService/*,ApplicationContextAware*/{ private static final Logger logger = LoggerFactory.getLogger(MkDiscountCardServiceImpl.class); @Autowired private ApplicationContext context;//上下文,用於發佈事件 @Override @Transactional public Long createDiscountCardAct(CreateMkDiscountCardDto dto) { /**..... 省略一堆邏輯 ....*/ //使用 spring 觀察者模式 異步處理 MkDiscountCardDto cardDto = BeanUtils.copyProps(entity, MkDiscountCardDto.class); TicketCodeEvent event = new TicketCodeEvent("",cardDto);//構建事件 this.context.publishEvent(event); //發佈事件 return id; }