服务器之家

服务器之家 > 正文

Tomcat和Spring中的事件机制深入讲解

时间:2021-06-23 14:07     来源/作者:knock_小新

引言

最近在看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参数为具体的事件关注内容

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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对象。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
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来存储感兴趣的监听对象,并在合适的时机会发布消息并通知所有监听对象。

?
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
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);
  }
 }
}

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

?
1
2
3
4
5
6
7
8
9
10
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等等,是事件监听者感兴趣的对象。

?
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
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的各个阶段处理进行相应处理。这些感兴趣的对象包括下面这些类:

Tomcat和Spring中的事件机制深入讲解

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

?
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
38
39
40
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类中出现太多管理监听对象的逻辑。

?
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
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的各个方法就可以了:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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方法就可以发布事件:

?
1
firelifecycleevent(lifecycle.configure_stop_event, null);

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

spring的事件机制

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

首先是eventobject,spring里面的主要实现是applicationevent:

Tomcat和Spring中的事件机制深入讲解

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

?
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
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接口,在事件发生时就会发起通知:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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这一对象。

?
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
38
39
40
41
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方法即可。

?
1
applicationcontext.publishevent(new applicationevent(new string("事件发布")) { });

总结

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

  • jdk中定义了eventobject和eventlistener两个接口,奠定了事件机制的基础。
  • tomcat额外提供了一个support类来对监听器的添加删除以及发布进行管理。
  • spring容器内部通过simpleapplicationeventmulticaster来发布各个事件,用户可以通过实现applicationlistener接口来监听自己感兴趣的容器事件。

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

好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对服务器之家的支持。

原文链接:http://www.cnblogs.com/konck/p/10134541.html

相关文章

热门资讯

2020微信伤感网名听哭了 让对方看到心疼的伤感网名大全
2020微信伤感网名听哭了 让对方看到心疼的伤感网名大全 2019-12-26
yue是什么意思 网络流行语yue了是什么梗
yue是什么意思 网络流行语yue了是什么梗 2020-10-11
背刺什么意思 网络词语背刺是什么梗
背刺什么意思 网络词语背刺是什么梗 2020-05-22
苹果12mini价格表官网报价 iPhone12mini全版本价格汇总
苹果12mini价格表官网报价 iPhone12mini全版本价格汇总 2020-11-13
2021德云社封箱演出完整版 2021年德云社封箱演出在线看
2021德云社封箱演出完整版 2021年德云社封箱演出在线看 2021-03-15
返回顶部