zoukankan      html  css  js  c++  java
  • Tomcat与Spring中的事件机制详解

    最近在看tomcat源码,源码中出现了大量事件消息,可以说整个tomcat的启动流程都可以通过事件派发机制串起来,研究透了tomcat的各种事件消息,基本上对tomcat的启动流程也就有了一个整体的认识。在这一基础上,联想到之前在看spring源码过程中也存在不少事件相关知识,于是想对这两个框架中的事件派发机制做一个简单的总结,加深理解。

    事件机制原理其实比较简单,抽象来看的话,设计模式中的观察者模式可以说是最经典的事件驱动机制的体现了,观察者和被观察者就体现了事件监听和事件派发的角色。还有各种MQ,其实也是事件机制的一种体现。

    理解tomcat和spring中的事件机制之前,让我们先从最基本的jdk中提供的事件机制开始说起。

    JDK中的事件机制

    JDK中对事件机制的各个角色提供了完善的抽象,主要包括3个角色:

    EventObject(事件关注内容):事件发布时需要关注的内容。jdk中提供了EventObject接口。

    EventListener(事件监听者):事件监听对象,也就是对EventObject感兴趣的对象。jdk中提供了EventListener接口。

    EventSource(事件源):发布事件的对象,可以在该对象中组册EventListener,然后在特定的条件下发布EventObject给已经注册的EventListener。

    事件的注册与发布,需要这三个对象协同工作,可以通过下面的例子来说明各个对象的作用:

    首先是事件关注内容对象MyEventObject,实现了EventObject接口。eventName参数为具体的事件关注内容

    public class MyEventObject extends EventObject {
    
    	private String eventName ;
    	
    	public MyEventObject (Object source, String eventName) {
    		
    		super(source);
    		this.setEventName(eventName);
    	}
    
    	public String getEventName() {
    		return eventName;
    	}
    
    	public void setEventName(String eventName) {
    		this.eventName = eventName;
    	}
    
    	private static final long serialVersionUID = 8374250957018011175L;
    }
    复制代码

    其次是事件监听接口MyEventListener,继承了EventListener,定义了一个myEvent接口用来发布事件,任何感兴趣的监听对象都可以实现该接口来监听。

    对MyEventObject感兴趣的监听者MyEventListenerImpl,实现了MyEventListener接口,当事件发布时会触发myEvent事件并收到MyEventObject对象。

    public interface MyEventListener extends EventListener {
    
    	public void myEvent(MyEventObject eventObject);
    }
    
    public class MyEventListenerImpl implements MyEventListener {
    
    	@Override
    	public void myEvent(MyEventObject eventObject) {
    
    		System.out.println("MyEventListenerImpl --- " + eventObject.getEventName());
    	}
    }
    复制代码

    最后是事件发布源对象MyEventSource,它可以注册多个事件监听对象,任何实现了MyEventListener接口的监听对象都可以注册,内部通过一个Set来存储感兴趣的监听对象,并在合适的时机会发布消息并通知所有监听对象。

    public class MyEventSource {
    
    	private Set<MyEventListener> myEventListeners = new HashSet<>();
    	
    	public void addListener(MyEventListener listener){
    		
    		this.myEventListeners.add(listener);
    	}
    	
    	public void removeListener(MyEventListener listener){
    		
    		this.myEventListeners.remove(listener);
    	}
    	
    	public void pushEvent(){
    		//dosomething
            //发布push event消息
    		notifyListener(new MyEventObject(this, "push event"));
    	}
    	
    	private void notifyListener(MyEventObject eventObject){
    		
    		for (MyEventListener myEventListener : myEventListeners) {
    			myEventListener.myEvent(eventObject);
    		}
    	}
    }
    复制代码

    之后可以通过一个启动类来注册并触发事件:

    public static void main(String[] args) {
        
        MyEventSource myEventSource = new MyEventSource();
        
        MyEventListenerImpl myEventListenerImpl = new MyEventListenerImpl();
        
        myEventSource.addListener(myEventListenerImpl);
        
        myEventSource.pushEvent();
    }
    复制代码

    MyEventObject定义了感兴趣的内容,MyEventListenerImpl是对MyEventObject感兴趣的监听者,MyEventSource会发布MyEventObject给所有组册的监听者,最后通过一个main来启动整个流程。

    明白了jdk中对事件机制的定义,再来看看tomcat和spring中的事件机制。

    Tomcat的事件机制

    tomcat的事件机制也离不开EventObject、EventListener以及EventSource三个对象,只不过在此基础上提供了更加抽象和便捷的操作。这里我挑选tomcat的生命周期接口对象Lifecycle来讲解整个事件发布流程:

    首先还是EventObject对象LifecycleEvent,这里只列出了核心代码。它的主要参数是Lifecycle,Lifecycle中定义了tomcat各个阶段的名称:before_init、after_init、start等等,是事件监听者感兴趣的对象。

    public final class LifecycleEvent extends EventObject {
    
        //......
    
        public LifecycleEvent(Lifecycle lifecycle, String type, Object data) {
    
            super(lifecycle);
            this.type = type;
            this.data = data;
        }
    
        //......
    }
    
    public interface Lifecycle {
        /**
         * The LifecycleEvent type for the "component after init" event.
         */
        public static final String BEFORE_INIT_EVENT = "before_init";
    
        /**
         * The LifecycleEvent type for the "component after init" event.
         */
        public static final String AFTER_INIT_EVENT = "after_init";
    
        /**
         * The LifecycleEvent type for the "component start" event.
         */
        public static final String START_EVENT = "start";
    
        //......
    }
    复制代码

    事件监听接口LifecycleListener,定义了lifecycleEvent方法用来传递监听者感兴趣的LifecycleEvent对象,监听者使用LifecycleEvent参数用来在tomcat的各个阶段处理进行相应处理。这些感兴趣的对象包括下面这些类:

    这里使用ContextConfig类为例,可以看到它实现了LifecycleListener接口。这个类在解析server.xml的时候用来监听StandardContext的各个阶段的事件,并做出相应处理:

    public interface LifecycleListener {
    
        public void lifecycleEvent(LifecycleEvent event);
    }
    
    public class ContextConfig implements LifecycleListener {
        
        //......
        @Override
        public void lifecycleEvent(LifecycleEvent event) {
    
            // Identify the context we are associated with
            try {
                context = (Context) event.getLifecycle();
            } catch (ClassCastException e) {
                log.error(sm.getString("contextConfig.cce", event.getLifecycle()), e);
                return;
            }
    
            // Process the event that has occurred
            if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {
                configureStart();
            } else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) {
                beforeStart();
            } else if (event.getType().equals(Lifecycle.AFTER_START_EVENT)) {
                // Restore docBase for management tools
                if (originalDocBase != null) {
                    context.setDocBase(originalDocBase);
                }
            } else if (event.getType().equals(Lifecycle.CONFIGURE_STOP_EVENT)) {
                configureStop();
            } else if (event.getType().equals(Lifecycle.AFTER_INIT_EVENT)) {
                init();
            } else if (event.getType().equals(Lifecycle.AFTER_DESTROY_EVENT)) {
                destroy();
            }
        }
    
        //......
    }
    复制代码

    LifecycleSupport是我们需要了解的主要对象,它是监听对象的一个管理类,原理其实和上面的例子差不多,对应了MyEventSource类的部分功能,方便EventSource类来管理监听对象。它把对监听对象的添加移除以及发布事件几个操作进行了统一管理,避免EventSource类中出现太多管理监听对象的逻辑。

    public final class LifecycleSupport {
    
        //......
    
        //监听对象集合
        private LifecycleListener listeners[] = new LifecycleListener[0];
        
        private final Object listenersLock = new Object(); // Lock object for changes to listeners
    
        //添加监听对象
        public void addLifecycleListener(LifecycleListener listener) {
    
          synchronized (listenersLock) {
              LifecycleListener results[] =
                new LifecycleListener[listeners.length + 1];
              for (int i = 0; i < listeners.length; i++)
                  results[i] = listeners[i];
              results[listeners.length] = listener;
              listeners = results;
          }
    
        }
    
        //发布监听对象
        public void fireLifecycleEvent(String type, Object data) {
    
            LifecycleEvent event = new LifecycleEvent(lifecycle, type, data);
            LifecycleListener interested[] = listeners;
            for (int i = 0; i < interested.length; i++)
                interested[i].lifecycleEvent(event);
    
        }
    
        //移除监听对象
        public void removeLifecycleListener(LifecycleListener listener) {
    
            synchronized (listenersLock) {
                int n = -1;
                for (int i = 0; i < listeners.length; i++) {
                    if (listeners[i] == listener) {
                        n = i;
                        break;
                    }
                }
                if (n < 0)
                    return;
                LifecycleListener results[] =
                  new LifecycleListener[listeners.length - 1];
                int j = 0;
                for (int i = 0; i < listeners.length; i++) {
                    if (i != n)
                        results[j++] = listeners[i];
                }
                listeners = results;
            }
        }
    }
    复制代码

    使用了LifecycleSupport之后,操作LifecycleListener就简单多了,只需要调用LifecycleSupport的各个方法就可以了:

    public abstract class LifecycleBase implements Lifecycle{
    
        //......
        private LifecycleSupport lifecycle = new LifecycleSupport(this);
    
        @Override
        public void addLifecycleListener(LifecycleListener listener) {
            lifecycle.addLifecycleListener(listener);
        }
    
        @Override
        public void removeLifecycleListener(LifecycleListener listener) {
            lifecycle.removeLifecycleListener(listener);
        }
    
        protected void fireLifecycleEvent(String type, Object data) {
            lifecycle.fireLifecycleEvent(type, data);
        }
        //......
    }
    复制代码

    在需要发布事件时调用fireLifecycleEvent方法就可以发布事件:

    fireLifecycleEvent(Lifecycle.CONFIGURE_STOP_EVENT, null);
    复制代码

    tomcat事件机制就是在之前的例子上抽出了一个LifecycleSupport类来方便管理监听对象的各种操作,这是一个可以借鉴的地方,其他差别并不大。再来看看spring中对事件机制的处理。

    Spring的事件机制

    spring中的事件机制原理也是一样的,只是相对来说实现上稍微复杂一点。还是通过相同的角度来看这个问题。

    首先是EventObject,spring里面的主要实现是ApplicationEvent:

    这里通过ContextStartedEvent类来查看EventObject,它关注的对象是ApplicationContext,是spring容器在启动时触发的事件对象:

    public abstract class ApplicationEvent extends EventObject {
    
        //......
    	public ApplicationEvent(Object source) {
    		super(source);
    		this.timestamp = System.currentTimeMillis();
    	}
        //......
    }
    
    public abstract class ApplicationContextEvent extends ApplicationEvent {
        public ApplicationContextEvent(ApplicationContext source) {
            super(source);
        }
    
        public final ApplicationContext getApplicationContext() {
            return (ApplicationContext)this.getSource();
        }
    }
    
    public class ContextStartedEvent extends ApplicationContextEvent {
        public ContextStartedEvent(ApplicationContext source) {
            super(source);
        }
    }
    复制代码

    事件监听接口ApplicationListener,定义了onApplicationEvent方法用来传递监听者感兴趣的ApplicationEvent对象,监听者使用ApplicationEvent参数用来在Context的各个阶段处理进行相应处理。

    如果我们需要在容器启动后进行相应处理,那么我们可以在业务类中实现ApplicationListener接口,在事件发生时就会发起通知:

    public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
    
    	void onApplicationEvent(E event);
    }
    
    public class MyApplicationListener implements ApplicationListener<ApplicationEvent> {
    
        @Override
        public void onApplicationEvent(ApplicationEvent applicationEvent) {
    
            if (applicationEvent instanceof ContextRefreshedEvent){
    
                System.out.println("context refresh!");   
            }
        }
    }
    复制代码

    那么在spring框架中是怎么发布这些事件的呢?是不是也有一个类似tomcat中LifecycleSupport一样的类呢?通过查看源码可以发现发现,ApplicationContext容器在初始化阶段会调用refresh()方法,这其中又调用了 finishRefresh()方法,这其中调用了publishEvent(new ContextRefreshedEvent(this))方法,发布了ContextRefreshedEvent这一对象。

    protected void finishRefresh() {
        
        //......
        // Publish the final event.
        publishEvent(new ContextRefreshedEvent(this));
    }
    
    protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
    
        //......
        getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
        //......
    }
    
    publishEvent方法通过调用一个默认的多播器SimpleApplicationEventMulticaster的multicastEvent方法来发布各种事件:
    
    SimpleApplicationEventMulticaster
    public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
        ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
    
        //通过getApplicationListeners获取了所有监听器,然后通过invokeListener方法循环发布事件
        for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
            Executor executor = getTaskExecutor();
            if (executor != null) {
                executor.execute(() -> invokeListener(listener, event));
            }
            else {
                invokeListener(listener, event);
            }
        }
    }
    
    protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
        //......
        doInvokeListener(listener, event);
    }
    
    private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
        //......
        listener.onApplicationEvent(event);
    }
    复制代码

    也就是说在spring容器中发布ApplicationListener所关注的对象是通过SimpleApplicationEventMulticaster这个类来管理的,和tomcat中LifecycleSupport的功能类似,只是在实现上有略微差别。

    最后提一句,在spring中你也可以自己发布各种事件,调用ApplicationContext的publishEvent方法即可。

    applicationContext.publishEvent(new ApplicationEvent(new String("事件发布")) { });
    复制代码

    总结

    这篇文章对Java的事件机制在tomcat以及spring框架中的实现做了一个简单总结和对比,你需要知道以下几点:

    1. JDK中定义了EventObject和EventListener两个接口,奠定了事件机制的基础。
    2. Tomcat额外提供了一个support类来对监听器的添加删除以及发布进行管理。
    3. Spring容器内部通过SimpleApplicationEventMulticaster来发布各个事件,用户可以通过实现ApplicationListener接口来监听自己感兴趣的容器事件。

    希望你通过这篇文章的学习可以对Java的事件机制有一个更深刻的认识,在实现自己的事件机制时有可以借鉴以及改进的地方。


    作者:knock_小新
    链接:https://juejin.im/post/5c17b9086fb9a049a81f3c81

  • 相关阅读:
    数据库的接口
    BionicThe README from the bionic/libc
    发现问题——创新的原动力
    使用ADO或ADO控件访问数据库
    游标、事务并发和锁三者之间的那点事
    处女座——菜鸟程序员的工程总结
    数据库的基础知识以及创建数据库
    《Team Geek》前言(中文,自己翻译的)
    万里长征,始于足下——菜鸟程序员的学习总结(一)
    与RMAN相关的动态性能视图
  • 原文地址:https://www.cnblogs.com/kkdn/p/10137212.html
Copyright © 2011-2022 走看看