目录

Spring事件Application Event

Spring 的事件(Application Event)为 Bean 与 Bean 之间的消息通信提供了支持。当一个 Bean 处理完一个任务之后,希望另一个 Bean 知道并能做相应的处理,这时我们就需要让另一个 Bean 监听当前 Bean 所发送的事件。(观察者模式) Spring 的事件需要遵循以下流程:

  • 自定义事件,继承 ApplicationEvent。

  • 定义事件监听器,实现 ApplicationListener。

  • 使用容器发布事件。

什么是ApplicationContext? 它是Spring的核心,Context我们通常解释为上下文环境,但是理解成容器会更好些。 ApplicationContext则是应用的容器。

Spring把Bean(object)放在容器中,需要用就通过get方法取出来。

ApplicationEvent

是个抽象类,里面只有一个构造函数和一个长整型的timestamp。

ApplicationListener

是一个接口,里面只有一个onApplicationEvent方法。

所以自己的类在实现该接口的时候,要实装该方法。

如果在上下文中部署一个实现了ApplicationListener接口的bean,

那么每当在一个ApplicationEvent发布到 ApplicationContext时, 这个bean得到通知。其实这就是标准的Observer设计模式

一、如何使用?

1、 建立event

 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
public class BookingCreatedEvent extends ApplicationEvent {

        private static final long serialVersionUID = 3039313222160544111L;

        private Booking booking;

        public BookingCreatedEvent(Object source) {

              super(source);

        }

        public BookingCreatedEvent(Object source, Booking booking) {

              super(source);

              this.booking = booking;

        }

        public Booking getBooking() {

              return booking;

        }

}

BookingCreatedEvent需要继承ApplicationEvent。

2、建立listener

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
@Component
public  class  BookingEventsListener  implements  ApplicationListener <BookingCreatedEvent> {

        private static final Logger log = Logger.getLogger();

        //listener实现

        public void onApplicationEvent(BookingCreatedEvent event) {

                  log.debug("bookingId:" + event.getBooking().getId());

                  //do something 

       }

}

BookingEventsListener 需要实现ApplicationListener 并重写onApplicationEvent方法。ApplicationListener带泛型,如果泛型参数为BookingCreatedEvent,则表示只监听BookingCreatedEvent类型的事件,如果泛型参数为ApplicationEvent ,则表示监听所有类型的事件。另外可以用@Component来注册组件,这样就不需要在spring的配置文件中指定了。

3、触发event

 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
@Service("bookingService")

@Repository

public class JpaBookingService implements BookingService, ApplicationContextAware {

        private ApplicationContext context;

        public void setApplicationContext(ApplicationContext applicationContext)throws BeansException {

              log.debug("Autowired applicationContext");

              this.context = applicationContext;

        }

        //省略的代码

        @Transactional

        public  void  persistBooking(Booking booking)throws HibernateException, SQLException {

               em.persist(booking);

              log.debug("fire BookingCreatedEvent");

              BookingCreatedEvent bookingCreatedEvent =newBookingCreatedEvent(this, booking);

              //触发event

              this.context.publishEvent(bookingCreatedEvent);

        }

}


触发要实现ApplicationContextAware,用于引入ApplicationContext,由于bookingService也 是spring组件,所以在系统启动的时候,ApplicationContext已经注入。也可以用如下方式直接注入 ApplicationContext。

1
2
@Autowired
private ApplicationContext applicationContext;

二、有什么好处?

解耦:

​ 如上例子,如果客人booking了hotel以后,系统要发email给客人,那我们就可以在listener的do something处加上发送email的代码。

​ 上面我们讲用@Component把listener注册成了spring的组件,这样listener的用途是在runtime的时候解耦。而如果我们把listener用配置文件的方式注册的话,主要用途是在部署的时候解耦。在实际应用中,两种情况都有。

​ 另外要注意的一点是,service和listener是同步的,在service中的persistBooking有注册 @Transactional的情况下,listener中的do something和service中的persistBooking是在同一个tansaction下。

​ 如果要做异步,需要通过MQ或者数据库中转。

附录

作者:守住阳光 链接:https://www.jianshu.com/p/e450cded3306