Spring5源码解析-Spring框架中的事件和监听器

事件和平时所用的回调思想在与GUI(JavaScript,Swing)相关的技术中很是流行。而在Web应用程序的服务器端,咱们不多去直接使用。但这并不意味着咱们没法在服务端去实现一个面向事件的体系结构。前端

在本文中,咱们将重点介绍Spring框架中的事件处理。首先,会先介绍下事件驱动编程这个概念。接着,咱们会将精力放在专门用于Spring框架中的事件处理之上。而后咱们会看到实现事件调度和监听的主要方法。最后,咱们将在Spring应用程序中展现如何使用基本的监听器。java

事件驱动编程web

在开始讨论事件驱动编程的编程方面以前,先来讲一个场景,用来帮助你们更好地理解 event-driven 这个概念。在一个地方只有两个卖衣服的商店A和B.在A店中,咱们的消费者须要一个一个的接受服务,即,同一时间只有一个客户能够购物。在B店里,能够容许几个客户同时进行购物,当有客户须要卖家的帮助时,他须要举起他的右手示意一下。卖家看到后会来找他,帮助其作出更好的选择。关于事件驱动( event-driven )编程这个概念经过上述场景描述总结后就是:经过作一些动做来做为对一些行为的回应。spring

如上所见,事件驱动的编程(也称为基于事件的编程)是基于对接收到的信号的反应的编程形式。这些信号必须以某种形式来传输信息。举一个简单例子: 点击按钮 。咱们将这些信号称为事件。这些事件能够经过用户操做(鼠标点击,触摸)或程序条件执行触发(例如:一个元素的加载结束能够启动另外一个操做)来产生。数据库

为了更好地了解,请看如下示例,模仿用户在GUI界面中的操做:编程

publicclassEventBasedTest{后端

@Test性能优化

publicvoidtest(){服务器

Mouse mouse =newMouse();架构

mouse.addListener(newMouseListener() {

@Override

publicvoidonClick(Mouse mouse){

System.out.println("Listener#1 called");

        mouse.addListenerCallback();

      }

    });

mouse.addListener(newMouseListener() {

@Override

publicvoidonClick(Mouse mouse){

System.out.println("Listener#2 called");

        mouse.addListenerCallback();

      }

    });

    mouse.click();

assertTrue("2 listeners should be invoked but only "+mouse.getListenerCallbacks()+" were", mouse.getListenerCallbacks() ==2);

  }

}

classMouse{

privateList listeners =newArrayList();

privateintlistenerCallbacks =0;

 

publicvoidaddListenerCallback(){

    listenerCallbacks++;

  }

 

publicintgetListenerCallbacks(){

returnlistenerCallbacks;

  }

 

publicvoidaddListener(MouseListener listener){

    listeners.add(listener);

  }

 

publicvoidclick(){

System.out.println("Clicked !");

for(MouseListener listener : listeners) {

listener.onClick(this);

    }

  }

}

interfaceMouseListener{

publicvoidonClick(Mouse source);

}

打印输出以下所示:

Clicked !

Listener#1 called

Listener#2 called

Spring中的Events

Spring基于实现org.springframework.context.ApplicationListener接口的bean来进行事件处理。这个接口中只有一个方法,onApplicationEvent用来当一个事件发送过来时这个方法来触发相应的处理。该接口能够经过指定须要接收的事件来实现(不懂看源码咯,源码里方法接收一个 event 做为参数)。由此,Spring会自动过滤筛选能够用来接收给定事件的监听器( listeners )。

/**

* Interface to be implemented by application event listeners.

* Based on the standard {@codejava.util.EventListener} interface

* for the Observer design pattern.

*

*

As of Spring 3.0, an ApplicationListener can generically declare the event type

 

* that it is interested in. When registered with a Spring ApplicationContext, events

* will be filtered accordingly, with the listener getting invoked for matching event

* objects only.

*

*@authorRod Johnson

*@authorJuergen Hoeller

*@param the specific ApplicationEvent subclass to listen to

*@seeorg.springframework.context.event.ApplicationEventMulticaster

*/

@FunctionalInterface

publicinterfaceApplicationListenerextendsEventListener{

/**

* Handle an application event.

*@paramevent the event to respond to

*/

voidonApplicationEvent(E event);

}

事件经过org.springframework.context.ApplicationEvent实例来表示。这个抽象类继承扩展了java.util.EventObject,可使用EventObject中的getSource方法,咱们能够很容易地得到所发生的给定事件的对象。这里,事件存在两种类型:

与应用程序上下文相关联:全部这种类型的事件都继承自 org.springframework.context.event.ApplicationContextEvent 类。它们应用于由org.springframework.context.ApplicationContext引起的事件(其构造函数传入的是 ApplicationContext 类型的参数)。这样,咱们就能够直接经过应用程序上下文的生命周期来获得所发生的事件: ContextStartedEvent 在上下文启动时被启动,当它中止时启动 ContextStoppedEvent ,当上下文被刷新时产生 ContextRefreshedEvent ,最后在上下文关闭时产生 ContextClosedEvent 。

/**

* Base class for events raised for an {@codeApplicationContext}.

*

*@authorJuergen Hoeller

*@since2.5

*/

@SuppressWarnings("serial")

publicabstractclassApplicationContextEventextendsApplicationEvent{

/**

* Create a new ContextStartedEvent.

*@paramsource the {@codeApplicationContext} that the event is raised for

* (must not be {@codenull})

*/

publicApplicationContextEvent(ApplicationContext source){

super(source);

}

/**

* Get the {@codeApplicationContext} that the event was raised for.

*/

publicfinalApplicationContextgetApplicationContext(){

return(ApplicationContext) getSource();

}

}

/**

* Event raised when an {@codeApplicationContext} gets started.

*

*@authorMark Fisher

*@authorJuergen Hoeller

*@since2.5

*@seeContextStoppedEvent

*/

@SuppressWarnings("serial")

publicclassContextStartedEventextendsApplicationContextEvent{

/**

* Create a new ContextStartedEvent.

*@paramsource the {@codeApplicationContext} that has been started

* (must not be {@codenull})

*/

publicContextStartedEvent(ApplicationContext source){

super(source);

}

}

/**

* Event raised when an {@codeApplicationContext} gets stopped.

*

*@authorMark Fisher

*@authorJuergen Hoeller

*@since2.5

*@seeContextStartedEvent

*/

@SuppressWarnings("serial")

publicclassContextStoppedEventextendsApplicationContextEvent{

/**

* Create a new ContextStoppedEvent.

*@paramsource the {@codeApplicationContext} that has been stopped

* (must not be {@codenull})

*/

publicContextStoppedEvent(ApplicationContext source){

super(source);

}

}

/**

* Event raised when an {@codeApplicationContext} gets initialized or refreshed.

*

*@authorJuergen Hoeller

*@since04.03.2003

*@seeContextClosedEvent

*/

@SuppressWarnings("serial")

publicclassContextRefreshedEventextendsApplicationContextEvent{

/**

* Create a new ContextRefreshedEvent.

*@paramsource the {@codeApplicationContext} that has been initialized

* or refreshed (must not be {@codenull})

*/

publicContextRefreshedEvent(ApplicationContext source){

super(source);

}

}

/**

* Event raised when an {@codeApplicationContext} gets closed.

*

*@authorJuergen Hoeller

*@since12.08.2003

*@seeContextRefreshedEvent

*/

@SuppressWarnings("serial")

publicclassContextClosedEventextendsApplicationContextEvent{

/**

* Creates a new ContextClosedEvent.

*@paramsource the {@codeApplicationContext} that has been closed

* (must not be {@codenull})

*/

publicContextClosedEvent(ApplicationContext source){

super(source);

}

}

与request 请求相关联:由 org.springframework.web.context.support.RequestHandledEvent 实例来表示,当在ApplicationContext中处理请求时,它们被引起。

Spring如何将事件分配给专门的监听器?这个过程由事件广播器( event multicaster )来实现,由 org.springframework.context.event.ApplicationEventMulticaster 接口的实现表示。此接口定义了3种方法,用于:

添加新的监听器:定义了两种方法来添加新的监听器: addApplicationListener(ApplicationListener listener) 和 addApplicationListenerBean(String listenerBeanName) 。当监听器对象已知时,能够应用第一个。若是使用第二个,咱们须要将bean name 获得listener对象( 依赖查找DL ),而后再将其添加到 listener 列表中。在这里顺便给你们推荐一个架构交流群:617434785,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源。相信对于已经工做和遇到技术瓶颈的码友,在这个群里会有你须要的内容。

删除监听器:添加方法同样,咱们能够经过传递对象来删除一个监听器( removeApplicationListener(ApplicationListener listener) 或经过传递bean名称( removeApplicationListenerBean(String listenerBeanName)) , 第三种方法,removeAllListeners()用来删除全部已注册的监听器

将事件发送到已注册的监听器:由multicastEvent(ApplicationEvent event)源码注释可知,它用来向全部注册的监听器发送事件。实现能够从 org.springframework.context.event.SimpleApplicationEventMulticaster中 找到,以下所示:

@Override

publicvoidmulticastEvent(ApplicationEvent event){

multicastEvent(event, resolveDefaultEventType(event));

}

@Override

publicvoidmulticastEvent(finalApplicationEvent event, @Nullable ResolvableType eventType){

ResolvableType type = (eventType !=null? eventType : resolveDefaultEventType(event));

for(finalApplicationListener listener : getApplicationListeners(event, type)) {

Executor executor = getTaskExecutor();

if(executor !=null) {

executor.execute(() -> invokeListener(listener, event));

}

else{

invokeListener(listener, event);

}

}

}

privateResolvableTyperesolveDefaultEventType(ApplicationEvent event){

returnResolvableType.forInstance(event);

}

/**

* Invoke the given listener with the given event.

*@paramlistener the ApplicationListener to invoke

*@paramevent the current event to propagate

*@since4.1

*/

@SuppressWarnings({"unchecked","rawtypes"})

protectedvoidinvokeListener(ApplicationListener listener, ApplicationEvent event){

ErrorHandler errorHandler = getErrorHandler();

if(errorHandler !=null) {

try{

listener.onApplicationEvent(event);

}

catch(Throwable err) {

errorHandler.handleError(err);

}

}

else{

try{

listener.onApplicationEvent(event);

}

catch(ClassCastException ex) {

String msg = ex.getMessage();

if(msg ==null|| msg.startsWith(event.getClass().getName())) {

// Possibly a lambda-defined listener which we could not resolve the generic event type for

Log logger = LogFactory.getLog(getClass());

if(logger.isDebugEnabled()) {

logger.debug("Non-matching event type for listener: "+ listener, ex);

}

}

else{

throwex;

}

}

}

}

咱们来看看 event multicaster 在应用程序上下文中所在的位置。在 AbstractApplicationContext 中定义的一些方法能够看到其中包含调用public void publishEvent方法。经过这种方法的注释可知,它负责向全部监听器发送给定的事件:

/**

* Publish the given event to all listeners.

*

Note: Listeners get initialized after the MessageSource, to be able

 

* to access it within listener implementations. Thus, MessageSource

* implementations cannot publish events.

*@paramevent the event to publish (may be application-specific or a

* standard framework event)

*/

@Override

publicvoidpublishEvent(ApplicationEvent event){

publishEvent(event,null);

}

/**

* Publish the given event to all listeners.

*

Note: Listeners get initialized after the MessageSource, to be able

 

* to access it within listener implementations. Thus, MessageSource

* implementations cannot publish events.

*@paramevent the event to publish (may be an {@linkApplicationEvent}

* or a payload object to be turned into a {@linkPayloadApplicationEvent})

*/

@Override

publicvoidpublishEvent(Object event){

publishEvent(event,null);

}

/**

* Publish the given event to all listeners.

*@paramevent the event to publish (may be an {@linkApplicationEvent}

* or a payload object to be turned into a {@linkPayloadApplicationEvent})

*@parameventType the resolved event type, if known

*@since4.2

*/

protectedvoidpublishEvent(Object event, @Nullable ResolvableType eventType){

Assert.notNull(event,"Event must not be null");

if(logger.isTraceEnabled()) {

logger.trace("Publishing event in "+ getDisplayName() +": "+ event);

}

// Decorate event as an ApplicationEvent if necessary

ApplicationEvent applicationEvent;

if(eventinstanceofApplicationEvent) {

applicationEvent = (ApplicationEvent) event;

}

else{

applicationEvent =newPayloadApplicationEvent<>(this, event);

if(eventType ==null) {

eventType = ((PayloadApplicationEvent)applicationEvent).getResolvableType();

}

}

// Multicast right now if possible - or lazily once the multicaster is initialized

if(this.earlyApplicationEvents !=null) {

this.earlyApplicationEvents.add(applicationEvent);

}

else{

getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);

}

// Publish event via parent context as well...

if(this.parent !=null) {

if(this.parentinstanceofAbstractApplicationContext) {

((AbstractApplicationContext)this.parent).publishEvent(event, eventType);

}

else{

this.parent.publishEvent(event);

}

}

}

该方法由如下方法调用:启动上下文(启动后发布 ContextStartedEvent ),中止上下文(中止后发布 ContextStoppedEvent ),刷新上下文(刷新后发布 ContextRefreshedEvent )并关闭上下文(关闭后发布 ContextClosedEvent ):

/**

* Finish the refresh of this context, invoking the LifecycleProcessor's

* onRefresh() method and publishing the

* {@linkorg.springframework.context.event.ContextRefreshedEvent}.

*/

protectedvoidfinishRefresh(){

// Clear context-level resource caches (such as ASM metadata from scanning).

clearResourceCaches();

// Initialize lifecycle processor for this context.

initLifecycleProcessor();

// Propagate refresh to lifecycle processor first.

getLifecycleProcessor().onRefresh();

// Publish the final event.生命周期Refreshed事件

publishEvent(newContextRefreshedEvent(this));

// Participate in LiveBeansView MBean, if active.

LiveBeansView.registerApedplicationContext(this);

}

/**

* Actually performs context closing: publishes a ContextClosedEvent and

* destroys the singletons in the bean factory of this application context.

*

Called by both {@codeclose()} and a JVM shutdown hook, if any.

 

*@seeorg.springframework.context.event.ContextClosedEvent

*@see#destroyBeans()

*@see#close()

*@see#registerShutdownHook()

*/

protectedvoiddoClose(){

if(this.active.get() &&this.closed.compareAndSet(false,true)) {

if(logger.isInfoEnabled()) {

logger.info("Closing "+this);

}

LiveBeansView.unregisterApplicationContext(this);

try{

// Publish shutdown event. ContextClosed事件

publishEvent(newContextClosedEvent(this));

}

catch(Throwable ex) {

logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", ex);

}

// Stop all Lifecycle beans, to avoid delays during individual destruction.

try{

getLifecycleProcessor().onClose();

}

        ...

      }     

 

//---------------------------------------------------------------------

// Implementation of Lifecycle interface

//---------------------------------------------------------------------

@Override

publicvoidstart(){

getLifecycleProcessor().start();

publishEvent(newContextStartedEvent(this));

}

@Override

publicvoidstop(){

getLifecycleProcessor().stop();

publishEvent(newContextStoppedEvent(this));

}

使用Spring的Web应用程序也能够处理与请求相关联的另外一种类型的事件(以前说到的 RequestHandledEvent )。它的处理方式和面向上下文的事件相似。首先,咱们能够找到org.springframework.web.servlet.FrameworkServlet中处理请求的方法processRequest。在这个方法结束的时候,调用了 private void publishRequestHandledEvent(HttpServletRequest request, HttpServletResponse response, long startTime, @Nullable Throwable failureCause) 方法。如其名称所表达的,此方法将向全部监听器发布给定的 RequestHandledEvent 。事件在传递给应用程序上下文的 publishEvent 方法后,将由 event multicaster 发送。这里没毛病,由于 RequestHandledEvent 扩展了与 ApplicationContextEvent 相同的类,即 ApplicationEvent 。来看看 publishRequestHandledEvent 方法的源码:

privatevoidpublishRequestHandledEvent(HttpServletRequest request, HttpServletResponse response,

longstartTime, @Nullable Throwable failureCause) {

//不少人问我Spring5和4的代码有什么区别,就在不少细微的地方,Spring一直在作不懈的改进和封装,很少说,没事可自行 //对比,能学到不少东西

if(this.publishEvents &&this.webApplicationContext !=null) {

// Whether or not we succeeded, publish an event.

longprocessingTime = System.currentTimeMillis() - startTime;

this.webApplicationContext.publishEvent(

newServletRequestHandledEvent(this,

request.getRequestURI(), request.getRemoteAddr(),

request.getMethod(), getServletConfig().getServletName(),

WebUtils.getSessionId(request), getUsernameForRequest(request),

processingTime, failureCause, response.getStatus()));

}

}

须要注意的是,你能够关闭基于请求的事件的调度。 FrameworkServlet的setPublishEvents(boolean publishEvents) 容许禁用事件分派,例如改进应用程序性能(看代码注释,当没有监听器来管理相应事件的时候,干吗要浪费性能)。默认状况下,事件调度被激活(默认为true)。

/** Should we publish a ServletRequestHandledEvent at the end of each request? */

privatebooleanpublishEvents =true;

/**

* Set whether this servlet should publish a ServletRequestHandledEvent at the end

* of each request. Default is "true"; can be turned off for a slight performance

* improvement, provided that no ApplicationListeners rely on such events.

*@seeorg.springframework.web.context.support.ServletRequestHandledEvent

*/

publicvoidsetPublishEvents(booleanpublishEvents){

this.publishEvents = publishEvents;

}

假若有思考的话,从上面的代码中能够知道,事件在应用程序响应性上的表现会不好(大都是一个调用另外一个)。这是由于默认状况下,它们是同步调用线程(即便用同一线程去处理事务,处理请求,以及准备视图的输出)。所以,若是一个监听器须要几秒钟的时间来响应,整个应用程序可能会受到慢的要死。幸运的是,咱们能够指定事件处理的异步执行(参考上面的 multicastEvent 源码)。须要注意的是,所处理的事件将没法与调用者的上下文(类加载器或事务)进行交互。这里参考 multicastEvent 方法源码便可。默认状况下,org.springframework.core.task.SyncTaskExecutor用来调用相应监听器。

publicclassSyncTaskExecutorimplementsTaskExecutor,Serializable{

/**

* Executes the given {@codetask} synchronously, through direct

* invocation of it's {@linkRunnable#run() run()} method.

*@throwsIllegalArgumentException if the given {@codetask} is {@codenull}

*/

@Override

publicvoidexecute(Runnable task){

Assert.notNull(task,"Runnable must not be null");

task.run();

}

}

在Spring中实现一个简单的监听器

为了更好的理解事件监听器,咱们来写一个小的测试用例。经过这个例子,咱们要证实默认状况下,监听器 listeners 在其调用者线程中执行了分发的事件。因此,为了避免当即获得结果,咱们在监听器中休眠5秒(调用Thread.sleep(5000))。测试检查是否达到3个目的:若是controller 的返回结果和所预期的视图名称相匹配,若是事件监听器花了5秒钟的时间才响应(Thread.sleep执行没有任何问题),而且若是controller 的一样花了5秒钟来生成视图(由于监听器的休眠)。

第二个定义的测试将验证咱们的监听器是否在另外一个事件中被捕获(和以前的类继承同一个类型)。首先,在配置文件中对bean的定义:

<--ThisbeanwillcatchSampleCustomEventlaunchedintestedcontroller-->

 

<--Thankstothisbeanwe'llabletogettheexecutiontimesoftestedcontrollerandlistener-->

 

事件和监听器的代码:

publicclassSampleCustomEventextendsApplicationContextEvent{

privatestaticfinallongserialVersionUID =4236181525834402987L;

publicSampleCustomEvent(ApplicationContext source){

super(source);

  }

}

publicclassOtherCustomEventextendsApplicationContextEvent{

privatestaticfinallongserialVersionUID =5236181525834402987L;

publicOtherCustomEvent(ApplicationContext source){

super(source);

  }

}

publicclassSampleCustomEventListenerimplementsApplicationListener{

@Override

publicvoidonApplicationEvent(SampleCustomEvent event){

longstart = System.currentTimeMillis();

try{

Thread.sleep(5000);

}catch(Exception e) {

      e.printStackTrace();

    }

longend = System.currentTimeMillis();

inttestTime = Math.round((end - start) /1000);

((TimeExecutorHolder) event.getApplicationContext().getBean("timeExecutorHolder")).addNewTime("sampleCustomEventListener",newInteger(testTime));

  }

}

没什么复杂的,事件只能被用来初始化。监听器经过获取当前时间(以毫秒为单位)来测试所执行时间,并在转换后保存(以秒为单位)。监听器使用的 TimeExecutorHolder 也不复杂:

publicclassTimeExecutorHolder{

privateMap testTimes =newHashMap();

 

publicvoidaddNewTime(String key, Integer value){

    testTimes.put(key, value);

  }

 

publicIntegergetTestTime(String key){

returntestTimes.get(key);

  }

}

此对象只保留测试元素的执行时间一个Map。测试的controller实现看起来相似于监听器。惟一的区别是它发布一个事件(接着被已定义的监听器捕获)并返回一个名为“success”的视图:

@Controller

publicclassTestController{

@Autowired

privateApplicationContext context;

 

@RequestMapping(value ="/testEvent")

publicStringtestEvent(){

longstart = System.currentTimeMillis();

context.publishEvent(newSampleCustomEvent(context));

longend = System.currentTimeMillis();

inttestTime = (int)((end - start) /1000);

((TimeExecutorHolder) context.getBean("timeExecutorHolder")).addNewTime("testController",newInteger(testTime));

return"success";

  }

@RequestMapping(value ="/testOtherEvent")

publicStringtestOtherEvent(){

context.publishEvent(newOtherCustomEvent(context));

return"success";

  }

}

最后,写一个测试用例,它调用/testEvent并在 TimeExecutorHolder bean 以后检查以验证两个部分的执行时间:

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration(locations={"file:applicationContext-test.xml"})

@WebAppConfiguration

publicclassSpringEventsTest{

@Autowired

privateWebApplicationContext wac;

privateMockMvc mockMvc;

@Before

publicvoidsetUp(){

this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();

  }

@Test

publicvoidtest(){

try{

MvcResult result = mockMvc.perform(get("/testEvent")).andReturn();

      ModelAndView view = result.getModelAndView();

String expectedView ="success";

assertTrue("View name from /testEvent should be '"+expectedView+"' but was '"+view.getViewName()+"'", view.getViewName().equals(expectedView));

}catch(Exception e) {

      e.printStackTrace();

    }

TimeExecutorHolder timeHolder = (TimeExecutorHolder)this.wac.getBean("timeExecutorHolder");

intcontrollerSec = timeHolder.getTestTime("testController").intValue();

inteventSec = timeHolder.getTestTime("sampleCustomEventListener").intValue();

assertTrue("Listener for SampleCustomEvent should take 5 seconds before treating the request but it took "+eventSec+" instead",  eventSec ==5);

assertTrue("Because listener took 5 seconds to response, controller should also take 5 seconds before generating the view, but it took "+controllerSec+" instead", controllerSec ==5);

  }

@Test

publicvoidotherTest(){

TimeExecutorHolder timeHolder = (TimeExecutorHolder)this.wac.getBean("timeExecutorHolder");

timeHolder.addNewTime("sampleCustomEventListener", -34);

try{

MvcResult result = mockMvc.perform(get("/testOtherEvent")).andReturn();

      ModelAndView view = result.getModelAndView();

String expectedView ="success";

assertTrue("View name from /testEvent should be '"+expectedView+"' but was '"+view.getViewName()+"'", view.getViewName().equals(expectedView));

}catch(Exception e) {

      e.printStackTrace();

    }

Integer eventSecObject = timeHolder.getTestTime("sampleCustomEventListener");

assertTrue("SampleCustomEventListener shouldn't be trigerred on OtherEvent but it was", eventSecObject.intValue() == -34);

  }

}

测试经过没有任何问题。它证实了咱们所设定的许多假设。

首先,咱们看到事件编程包括在信号发送到应用程序时触发并执行某些操做。这个信号必须有一个监听器在监听。在Spring中,因为监听器中的泛型定义( void onApplicationEvent(E event); ),事件能够很容易地被 listeners 所捕获。经过它,若是所触发的事件对应于监听器所预期的事件,咱们无须多余的检查(说的啰嗦了,就是符合所需求的类型便可,省去不少麻烦,咱们能够直接根据泛型就能够实现不少不一样的处理)。咱们还发现,默认状况下,监听器是以同步方式执行的。因此在调用线程同时执行好比视图生成或数据库处理的操做是不行的。

最后,要说的是,算是一个先后端通用的思想吧,所谓的事件,其实想来,不过是一个接口而已,把这个接口派发出去(event multicaster),由谁来实现,这是他们的事情,这里就有一个装饰类(这么理解就好),其名字叫listener,拿到这个派发的事件接口,而后调用相应的实现,这里为了程序的更加灵活和高可用,咱们会调用相应的adapter适配器,最后调用其相应的Handler实现,而后Handler会调用相应的service,service调用dao。

一样这个思想用在前端就是组件对外派发一个事件,这个事件由其父组件或者实现,或者继续向外派发,最后用一个具体的方法将之实现便可

其实对应于咱们的数学来说就是,咱们定义一个数学公式f(x)*p(y)同样,这个派发出去,不管咱们先实现了f(x)仍是先实现了p(y),仍是一次全实现,仍是分几回派发出去,终究咱们会在最后去将整个公式彻底解答出来,这也就是所谓的事件机制,难么?

相关文章
相关标签/搜索