怎么打开

Spring的事件和监听器-同步与异步 - 菜鸟之家 - CSDN博客
Spring的事件和监听器-同步与异步
Java Develop
Application下抽象子类ApplicationContextEvent的下面有4个已经实现好的事件&
ContextClosedEvent(容器关闭时)&ContextRefreshedEvent(容器刷新是)&ContextStartedEvent(容器启动时候)&ContextStoppedEvent(容器停止的时候)&
同样,这四个事件都继承了ApplicationEvent,如果我们想自定义事件,也可以通过继承ApplicationEvent来实现
1: 首先新建StartWorkflowEvent.java,&继承ApplicationEvent抽象类
public class StartWorkflowEvent extends ApplicationEvent {
//存放构造器送入的值
//构造器参数可以随意设置,这里为了方便调试,设置为字符串
public StartWorkflowEvent (String msg) {
super(msg);
//自定义一个方法,这个方法也可以随意写,这里也是测试用
public void myevent(){
System.out.println(&********My event**************&);
System.out.println(msg);
System.out.println(&*******************************&);
2:新建一个监听器StartWorkflowListener.java, 实现ApplicationListener&StartWorkflowEvent&
* 发起流程事件监听
@Component(&startWorkflowListener&)
public class StartWorkflowListener implements ApplicationListener&StartWorkflowEvent& {
@Autowired
private OaWorkflowHepler oaWorkflowH
//@Async注解异步调用时使用, 异步调用时, 需要在xml配置文件中添加 &task:annotation-driven /&
public void onApplicationEvent(StartWorkflowEvent event) {
oaWorkflowHepler.start(event.getMsg());
3:创建一个事件发布类EventPublisher.java
* 发布事件
@Component(&eventPublisher&)
public class EventPublisher {
@Autowired
private ApplicationContext applicationC
* 发布事件
* @param event
public void publishEvent(ApplicationEvent event) {
applicationContext.publishEvent(event);
4.相关的配置:
&task:annotation-driven /&配置:
executor:指定一个缺省的executor给@Async使用。
&task:annotation-driven executor=&asyncExecutor& /&
&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中最旧的那个任务
18CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
我的热门文章
即使是一小步也想与你分享SpringBoot之异步事件 - flappy tortoise - ITeye博客
博客分类:
为什么要使用异步事件呢?就是为了快速响应前端的请求,让那些耗时较长的任务在后台独立执行,比如大量数据的导出,邮件发送等等。使用异步事件,可以很好地提高用户的体验。那么异步事件该如何使用呢,分为如下几步:
1.定义事件
定义一个事件类,继承ApplicationEvent;
import org.springframework.context.ApplicationE
import lombok.G
import lombok.S
import lombok.ToS
* 自定义spring事件基类,所有的事件继承该基类
public class CommonEvent extends ApplicationEvent {
private static final long serialVersionUID = -9348221L;
/** 事件的内容(具有实际业务意义的内容) */
private String eventC
public CommonEvent(Object source) {
super(source);
public CommonEvent(Object source, String eventContent) {
super(source);
this.eventContent = eventC
* spring容器事件,封装所有的运行时异常事件
public class BatchSendExMsgEmailEvent extends CommonEvent {
public BatchSendExMsgEmailEvent(Object source) {
super(source);
private static final long serialVersionUID = -4523969L;
2.发布事件
发布一个事件,使用ApplicationContext.publish(event);
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
ex.printStackTrace(pw);
// 发布运行时异常事件
BatchSendExMsgEmailEvent event = new BatchSendExMsgEmailEvent(this);
event.setEventContent(sw.toString());
applicationContext.publishEvent(event);
3.监听事件并且进行处理
import org.slf4j.L
import org.slf4j.LoggerF
import org.springframework.beans.factory.annotation.A
import org.springframework.beans.factory.annotation.V
import org.springframework.context.event.EventL
import org.springframework.scheduling.annotation.A
import org.
@Component
public class ContainerEventListener {
private static final Logger logger = LoggerFactory.getLogger(ContainerEventListener.class);
@Autowired
private EmailService emailS
@Value("${email_receivers}")
@Value("${email_subject}")
private String emailS
//@Async注解指定该事件异步执行,必须配合启动类的@EnableAsync
@EventListener
public void handleBatchSendExMsgEmailEvent(BatchSendExMsgEmailEvent event) {
("监听到了运行时异常事件");
emailService.batchSendSimpleMail(receivers, emailSubject, "具体的异常信息为: " + event.getEventContent());
wuhoujian322
浏览: 21579 次
来自: 杭州
那么完美的实现方式应该是怎样的呢?
这样防止不了反射侵入
Redis我也是刚开始学习,它是一个内存数据库,不像传统的关系 ...
Redis不熟悉,主要的应用场景是什么?
大家遇到的问题也可以发出来,共同研究研究,一起进步。在Spring中使用异步事件实现同步事务
& & & &&& & &
结合Scala+Spring,我们将采取一个很简单的场景:下订单,然后发送一封电子邮件。
编制一个服务:
class OrderService @Autowired() (orderDao:
OrderDao, mailNotifier: OrderMailNotifier) {
@Transactional
def placeOrder(order: Order) {
orderDao save order //保存订单
&&& &&&&mailNotifier sendMail order //发送邮件
上面代码是在保存订单和发送邮件两个同步执行,发送邮件需要连接邮件服务器,比较耗时,拖延了整个性能,我们采取异步发送电子邮件,利用Spring内置的自定义事件,与JMS或其他生产者 - 消费者类似。
case class OrderPlacedEvent(order: Order)
extends ApplicationEvent
class OrderService @Autowired() (orderDao:
OrderDao, eventPublisher: ApplicationEventPublisher) {
@Transactional
def placeOrder(order: Order) {
orderDao save order
eventPublisher publishEvent OrderPlacedEvent(order)
区别是继承了ApplicationEvent 之前是直接用&OrderMailNotifier&直接发送,而现在我们使用&发送发邮件事件了。
事件监听者代码如下:
class OrderMailNotifier extends
ApplicationListener[OrderPlacedEvent] {
def onApplicationEvent(event: OrderPlacedEvent) {
//sending e-mail...
在监听者方法中真正实现邮件发送。
但是Spring的ApplicationEvents是同步事件,意味着我们并没有真正实现异步,程序还会在这里堵塞,如果希望异步,我们需要重新定义一个ApplicationEventMulticaster,实现类型SimpleApplicationEventMulticaster和TaskExecutor:
def applicationEventMulticaster() = {
val multicaster = new SimpleApplicationEventMulticaster()
multicaster.setTaskExecutor(taskExecutor())
multicaster
def taskExecutor() = {
val pool = new ThreadPoolTaskExecutor()
pool.setMaxPoolSize(10)
pool.setCorePoolSize(10)
pool.setThreadNamePrefix(&Spring-Async-&)
Spring通过使用TaskExecutor已经支持广播事件了,对onApplicationEvent()&标注&
def onApplicationEvent(event: OrderPlacedEvent) {
如果你希望使用@Async,可以编制自己的异步执行器:
@Configuration
@EnableAsync
class ThreadingConfig extends
AsyncConfigurer {
def getAsyncExecutor = taskExecutor()
def taskExecutor() = {
val pool = new ThreadPoolTaskExecutor()
pool.setMaxPoolSize(10)
pool.setCorePoolSize(10)
pool.setThreadNamePrefix(&Spring-Async-&)
@ EnableAsync是足够了。,默认情况下,Spring使用SimpleAsyncTaskExecutor类创建新的线程。
以上所有设置暴露一个真正的问题。现在,我们虽然使用其他线程发送一个异步消息处理。不幸的是,我们引入竞争条件。
存储order到数据库
发送一个包装order的消息
异步线程获得OrderPlacedEvent并开始处理。现在的问题是,它发生(3)之后,还是(4)之前或者(4)之后?这有一个很大的区别!在前者的情况下,交易也尚未提交订单所以不存在于数据库中。另一方面,延迟加载可能已经在工作,致使订单对象仍然然绑定在 PersistenceContext(缺省我们使用JPA)。
解决办法是使用&.,可以注册很多监听者&,它对于事务的提交或回滚都有事件发送。
@Transactional
def placeOrder(order: Order) {
orderDao save order
afterCommit {
eventPublisher publishEvent OrderPlacedEvent(order)
private def afterCommit[T](fun: =& T) {
TransactionSynchronizationManager.registerSynchronization(new
TransactionSynchronizationAdapter {
override def afterCommit() {
&&&&&&&&&&&
当前事务提交后 afterCommit()接受调用,可以安全地调用registerSynchronization()多次 - 监听器存储在Set并且本地保存到当前事务中,事务提交后消失。
我们将afterCommit方法单独抽象成一个类,分离关注。
TransactionAwareApplicationEventPublisher(delegate: ApplicationEventPublisher)
extends ApplicationEventPublisher {
override def publishEvent(event: ApplicationEvent) {
if (TransactionSynchronizationManager.isActualTransactionActive) {
&&&&&&&&&&&
TransactionSynchronizationManager.registerSynchronization(
&&&&&&&&&&&&&&& new
TransactionSynchronizationAdapter {
&&&&&&&&&&&&&&&&&&& override def afterCommit()
&&&&&&&&&&&&&&&&&&&&&&& delegate publishEvent
&&&&&&&&&&&&&&&&&&& }
&& &&&&&&&&&&&&&})
&&&&&&&&&&&
delegate publishEvent event
TransactionAwareApplicationEventPublisher是实现Spring的ApplicationEventPublisher。
我们要将这个新的实现告诉Spring替换掉旧的,用@Primary:
val applicationContext: ApplicationContext
transactionAwareApplicationEventPublisher() =
new TransactionAwareApplicationEventPublisher(applicationContext)
再看看原来的订单服务:
class OrderService @Autowired() (orderDao:
OrderDao, eventPublisher: ApplicationEventPublisher) {
@Transactional
def placeOrder(order: Order) {
orderDao save order
eventPublisher publishEvent OrderPlacedEvent(order)
注意这里ApplicationEventPublisher已经是我们自己实现的TransactionAwareApplicationEventPublisher,将被自动注入这个服务。
最后,要在真正订单保存的业务代码上放置事务:
def placeOrder(order: Order) {
storeOrder(order)
eventPublisher publishEvent OrderPlacedEvent(order)
@Transactional
def storeOrder(order: Order) = orderDao
save order
当然这没有根本解决问题,如果placeOrder有一个更大的事务怎么办?
| 网站地图 | 设为首页}

我要回帖

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信