Spring的监听事件ApplicationListener和ApplicationEvent及@EventListener用法

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

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

 

ApplicationEvent

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

ApplicationListener

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

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

 

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

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

 

下面给出例子:

首先创建一个ApplicationEvent实现类:

复制代码
 1 package com.spring.event;
 2 
 3 import org.springframework.context.ApplicationEvent;
 4 
 5 public class EmailEvent extends ApplicationEvent {
 6     /**
 7      * <p>Description:</p>
 8      */
 9     private static final long serialVersionUID = 1L;
10     public String address;  
11     public String text;
12     
13     public EmailEvent(Object source) {
14         super(source);
15     }
16     
17     public EmailEvent(Object source, String address, String text) {
18         super(source);
19         this.address = address;
20         this.text = text;
21     }
22     
23     public void print(){
24         System.out.println("hello spring event!");
25     }
26 
27 }
复制代码

给出监听器:

复制代码
 1 package com.spring.event;
 2 
 3 import org.springframework.context.ApplicationEvent;
 4 import org.springframework.context.ApplicationListener;
 5 public class EmailListener implements ApplicationListener {
 6 
 7     public void onApplicationEvent(ApplicationEvent  event) {
 8         if(event instanceof EmailEvent){
 9             EmailEvent emailEvent = (EmailEvent)event;
10             emailEvent.print();
11             System.out.println("the source is:"+emailEvent.getSource());
12             System.out.println("the address is:"+emailEvent.address);
13             System.out.println("the email's context is:"+emailEvent.text);
14         }
15         
16     }
17 
18 }
复制代码

applicationContext.xml文件配置:

<bean id="emailListener" class="com.spring.event.EmailListener"></bean>

 

测试类:

复制代码
 1 package com.spring.event;
 2 
 3 import org.springframework.context.ApplicationContext;
 4 import org.springframework.context.support.ClassPathXmlApplicationContext;
 5 
 6 public class Test {
 7     public static void main(String[] args) {
 8         ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
 9         
10         //HelloBean hello = (HelloBean) context.getBean("helloBean");
11         //hello.setApplicationContext(context);
12         EmailEvent event = new EmailEvent("hello","boylmx@163.com","this is a email text!");
13         context.publishEvent(event);
14         //System.out.println();
15     }
16 }
复制代码

测试结果

hello spring event!  
the source is:hello  
the address is:boylmx@163.com  
the email's context is:this is a email text!  

 

不积跬步无以至千里

Spring 4.2框架中注释驱动的事件监听器详解

作者:chszs,版权所有,未经同意,不得转载。博主主页:http://blog.csdn.net/chszs

事件交互已经成为很多应用程序不可或缺的一部分,Spring框架提供了一个完整的基础设施来处理瞬时事件。下面我们来看看Spring 4.2框架中基于注释驱动的事件监听器。

1、早期的方式

在早期,组件要从Spring事件获知自定义域事件中获取通知,那么组件必须实现ApplicationListener接口并覆写onApplicationEvent方法。

@Component
class OldWayBlogModifiedEventListener implements
                        ApplicationListener<OldWayBlogModifiedEvent> {
    (...)
    @Override
    public void onApplicationEvent(OldWayBlogModifiedEvent event) {
        externalNotificationSender.oldWayBlogModified(event);
    }
}

  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

上面的代码工作正常,但是它会针对每一个事件都创建一个新类,从而造成代码瓶颈。

另外,我们的事件类继承了ApplicationEvent类——Spring应用中的事件基类。

class OldWayBlogModifiedEvent extends ApplicationEvent {
    public OldWayBlogModifiedEvent(Blog blog) {
        super(blog);
    }
    public Blog getBlog() {
        return (Blog)getSource();
    }
}

  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

请注意,在事件中使用Domain域对象有明显的缺点,在一些场景下不可用。这里只是做代码示例。

顺便说一句,ExternalNotificationSender对象负责发送外部通知给已注册的用户(例如通过电子邮件、短信等方式)。

2、注释驱动的事件监听器

Spring 4.2框架值得注意的一点,用注释@EventListener注解任意的Spring组件。

//@EventListener(condition = "#sendSmsEvent.sender == 'andy1'")可以使用condition进行event属性过滤
@EventListener
public void blogModified(BlogModifiedEvent blogModifiedEvent) {
    externalNotificationSender.blogModified(blogModifiedEvent);
}

  
  
  • 1
  • 2
  • 3
  • 4
  • 5

Spring会为事件创建一个ApplicationListener实例,并从方法参数中获取事件的类型。一个类中被事件注释的方法数量没有限制,所有相关的事件句柄都会分组到一个类中。

3、有条件的事件处理

为了使注释@EventListener的功能更强大,Spring 4.2支持用SpEL表达式表达事件类型的方式。假设以下是事件类:

public class BlogModifiedEvent {
    private final Blog blog;
    private final boolean importantChange;
    public BlogModifiedEvent(Blog blog) {
        this(blog, false);
    }
    public BlogModifiedEvent(Blog blog, boolean importantChange) {
        this.blog = blog;
        this.importantChange = importantChange;
    }
    public Blog getBlog() {
        return blog;
    }
    public boolean isImportantChange() {
        return importantChange;
    }
}

  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

要注意,在实际应用中可能不会有本文这样的层次结构的事件。 
还要注意,用Groovy编写会更加简单。

使用条件参数来阐述事件,重要的变化是:

@EventListener(condition = "#blogModifiedEvent.importantChange")
public void blogModifiedSpEL(BlogModifiedEvent blogModifiedEvent) {
    externalNotificationSender.blogModifiedSpEL(blogModifiedEvent);
}

  
  
  • 1
  • 2
  • 3
  • 4
  • 5

4、宽松事件类型的层次结构

Spring 4.2之前的版本,ApplicationEventPublisher只有在ApplicationEvent事件后发布其继承对象的能力。而在Spring 4.2版开始,此接口已经扩展到支持任意对象类型。在这种情况下,对象被封装到PayloadApplicationEvent和通过发送。

//base class with Blog field - no need to extend `ApplicationEvent`
class BaseBlogEvent {}
class BlogModifiedEvent extends BaseBlogEvent {}
//somewhere in the code
ApplicationEventPublisher publisher = (...);    //injected
publisher.publishEvent(new BlogModifiedEvent(blog)); //just plain instance of the event

  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

这一变化使得发布事件更容易。然而另一方面它可以导致事件跟踪变得更加困难,特别是在大型应用程序中。

5、响应发布事件

注释@EventListener还有一点需注意,在非空返回类型时,Spring会自动发布返回的事件。

@EventListener
public BlogModifiedResponseEvent blogModifiedWithResponse(BlogModifiedEvent blogModifiedEvent) {
    externalNotificationSender.blogModifiedWithResponse(blogModifiedEvent);
    return new BlogModifiedResponseEvent(
        blogModifiedEvent.getBlog(), BlogModifiedResponseEvent.Status.OK);
}

  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

6、异步事件处理

注释@EventListener还可以与注释@Async进行组合使用,以提供异步事件处理的机制。下面的代码中,指定的事件监听器既不会阻塞主要的代码执行,又不会被其它的监听器处理。

@Async    //Remember to enable asynchronous method execution 
          //in your application with @EnableAsync
@EventListener
public void blogAddedAsync(BlogAddedEvent blogAddedEvent) {
    externalNotificationSender.blogAdded(blogAddedEvent);
}

  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

为了使工作能够得到异步执行,通常还需在Spring项目的上下文中使用注释@EnableAsync。

7、总结

注释驱动的事件监听器是Spring框架4.2版中引入的新特性,它减少了Spring项目的样板代码,使得代码更加灵活,尤其是在小数量事件的需求时体现更为明显。


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值