spring 事物监听机制,同步异步处理

来源:互联网 发布:心理测试 软件 编辑:程序博客网 时间:2024/06/07 09:22

一、概念原理:

事件传播
ApplicationContext基于Observer模式(java.util包中有对应实现),提供了针对Bean的事件传
播功能。通过Application. publishEvent方法,我们可以将事件通知系统内所有的ApplicationListener。
事件传播可用于异步通知调用及一些监控和异常处理,则通过事件传播
机制通知异常监听器进行处理。



spring_event

  • SUBJECT
    • 目标知道它的观察者。可以有任意多个观察者观察同一个目标。
    • 提供注册和删除观察者对象的接口。
  • Observer(观察者)
    • 为那些在目标发生改变时需获得通知的对象定义一个更新接口。
  • ConcreteSubject(具体目标)
    • 将有关状态存入各ConcreteObserver对象。
    • 当它的状态发生改变时,向它的各个观察者发出通知。
  • ConcreteObserver(具体观察者)
    • 维护一个指向ConcreteSubject对象的引用。
    • 存储有关状态,这些状态应与目标的状态保持一致。
    • 实现Observer的更新接口以使自身状态与目标的状态保持一致。

实现一个事件监听需要三个步骤:创建一个事件ApplicationEvent用来定义自己需要的事件,创建一个ApplicationListener用来监听并处理发布的事件, 用spring的应用上下文 ApplicationContext  来 publish发布 一个事件。

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

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

 

ApplicationEvent

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

ApplicationListener

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

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

 

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

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

二、具体实现和注意事项:

1、创建事件ApplicationEvent:

public class FileEnvent extends ApplicationEvent {    private static final long serialVersionUID = 1L;    String filePath;        public FileEnvent(Object source, String filePath) {        super(source);        this.filePath = filePath;    }    public String getFilePath() {        return filePath;    }    public void setFilePath(String filePath) {        this.filePath = filePath;    }}

注意:这里需要继承ApplicationEvent ,不过Spring也提供了几个内置事件:

          1、  ContextRefreshedEventApplicationContext容器初始化或者刷新时触发该事件。

          2、  ContextStartedEvent:当使用ConfigurableApplicationContext接口的start()方法启动ApplicationContext容器时触发该事件。

          3、  ContextClosedEvent:当使用ConfigurableApplicationContext接口的close()方法关闭ApplicationContext容器时触发该事件。

          4、  ContextStopedEvent: 当使用ConfigurableApplicationContext接口的stop()方法停止ApplicationContext容器时触发该事件。

2、创建监听器ApplicationListener

@Componentpublic class FileListener implements ApplicationListener<ApplicationEvent>{
      @Override    public void onApplicationEvent(ApplicationEvent event) {        if(event instanceof FileEnvent){            FileEnvent fileEnvent = (FileEnvent) event;            System.out.println( "file path is:" + event.getFilePath());  
        }    }
}
3、获取ApplicationContext

@Componentpublic class FileApplicationContext implements ApplicationContextAware {        private static ApplicationContext ctx;     @SuppressWarnings("static-access")    @Override    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {        this.ctx = applicationContext;    }    public static ApplicationContext getCtx() {        return ctx;    }}
这里通过继承ApplicationContextAware来获得ApplicationContext,当然,这个类也需要加载为spring,通过注解和配置文件配置都可以,

这里用注解也是为了解耦。

注意:这里有两个问题:

1)需要使用注解@Component 加载为spring的容器,也可以在配置文件里面配置,这里用注解是为了和配置文件解耦。

2)用@Component 会有两次执行onApplicationEvent()方法的可能,解决onApplicationEvent(方法被执行两次以上的问题:

原因:

      在web 项目中(spring mvc),系统会存在两个容器,一个是root application context ,另一个就是我们自己的 projectName-servlet  context(作为root application context的子容器)。 这种情况下,就会造成onApplicationEvent方法被执行两次。

解决方法:

1.需要注意的是,把@Component 改为@Controller,这样才表示是一个Spring的Bean(否则在相关Spring的xml中配置),才能被Spring容器处理。

2.若是web项目则可通过ContextLoader.getCurrentWebApplicationContext();方法来获取上下文容器。

上面获取ApplicationContext可以改成下面的实现:


public class WebContextUtil {
private static WebApplicationContext wac;public synchronized static WebApplicationContext getContext() {if( null == wac ) {wac = ContextLoader.getCurrentWebApplicationContext();}return wac;}}

4、发布事件

public static void main(){FileEnvent fileEnvent = new FileEnvent("", filePath);    WebContextUtil.getContext().publishEvent(fileEnvent);}

若不是web项目,可以用下面的方法,但是记得监听器用@Controller注解或在配置文件里面配置监听器

public static void main(){FileEnvent fileEnvent = new FileEnvent("", filePath);
      FileApplicationContext .getCtx().publishEvent(fileEnvent);}


三、异步实现事件的监听:

注意上面的ApplicationListener 中的监听机制为同步执行,即若发布两个事件,则必须等待前一个事件完成才能继续执行下一个事件,这里可以通过

spring的异步机制来实现:spring3.0版本开始支持@Async注解来实现异步调用。
1、首先通过在配置文件中配置使得注解生效
    <!-- 任务执行器 -->  <task:executor id="executor" pool-size="10"/><!--开启注解调度支持 @Async @Scheduled-->  <task:annotation-driven executor="executor"  proxy-target-class="true"/> 

2、在监听方法加上注解即可实现异步调用

@Componentpublic class FileListener implements ApplicationListener<ApplicationEvent>{@Async @Override    public void onApplicationEvent(ApplicationEvent event) {        if(event instanceof FileEnvent){            FileEnvent fileEnvent = (FileEnvent) event;            System.out.println( "file path is:" + event.getFilePath());          }    }}



通过以上方法即可实现同步或者异步的事件监听机制,非常实用。

部分内容参考:http://blog.csdn.net/sd0902/article/details/8394082




原创粉丝点击