JAVA事件监听机制和 Spring 事件机制

11 篇文章 0 订阅

JAVA事件监听机制和 Spring 事件机制

JAVA事件监听机制

在这里插入图片描述

​ Java 事件处理采用的是面向对象方法,所有的事件都是由 java.util包中的EventObject类扩展而来的 ( 公共超类不是 Event , 它是旧事件模型中的事件类名 。 尽管现在不赞成使用旧的事件模型, 但这些类仍然保留在Java 库中 )。

事件对象封装了事件源与监听器彼此通信的事件信息。 在必要的时候 ,可以对传递给监听器对象的事件对象进行分析。 在按钮例子中 , 是借助 getSourcegetActionCommand方法实现对象分析的。

事件源有一些向其注册事件监听器的方法。 当某个事件源产生事件时 ,事件源会向为事件注册的所有事件监听器对象发送一个通告。

事件监听模型:

  • 监听者(扩展 java.util.EventListener
    • 监听器对象是一个实现了特定监听器接口 ( listener interface ) 的类的实例。
    • 监听器对象将利用事件对象中的信息决定如何对事件做出响应。
    • 方法参数类型
      • java.util.EventObject 对象(子类)
      • 事件源
    • 监听方法访问限定符 public
    • 监听方法是没有返回值(void
      • 例外:Spring @EventListener
    • 监听方法不会 throws Throwable
  • 事件(扩展java.util.EventObject
    • 每个事件对象都有事件源。
  • 事件源(Object)
    • 事件源是一个能够注册监听器对象并发送事件对象的对象。

总结:当事件发生时,事件源将事件对象传递给所有注册的监听器 。

多事件监听器接口

限制:Java 8 之前,接口没有 default method,当出现多个监听方法时,需要 Adapter 抽象类提供空实现。

适配器类。

注意:事件/监听者器,尤其在扩展时,注意事件(语法)时态。

public interface WindowListener extends EventListener { 
    public void windowOpened(WindowEvent e);
    public void windowClosing(WindowEvent e);   
    public void windowClosed(WindowEvent e);   
    public void windowIconified(WindowEvent e);  
    public void windowDeiconified(WindowEvent e);  
    public void windowActivated(WindowEvent e);
    public void windowDeactivated(WindowEvent e);
}

​ 实现接口,书写 6 个没有任何操作的方法代码显然是一种乏味的工作 。鉴于简化的目的,每个含有多个方法的 AWT 监听器接口都配有一个适配器 ( adapter ) 类, 这个类实现了接口中的所有方法 , 但每个方法没有做任何事情。 这意味着适配器类自动地满足了Java 实现相关监听器接口的技术需求。 可以通过扩展适配器类来指定对某些事件的响应动作 , 而不必实现接口中的每个方法 ( ActionListener 这样的接口只有一个方法, 因此没必要提供适配器类 ) 。

class Terminator extends WindowAdapter{
    public void windowCIosing( WindowEvent e ){      
        System.exit(O) ;
    }
}

awt button 事件实例

public class ButtonFrame  extends JFrame{
    
    private JPanel buttonPanel;
    private static final int DEFAULT_WIDTH = 300 ;
    private static final int DEFAULT_HEIGHT = 300 ;
    public ButtonFrame(){
        // 设置大小
        setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);

        // 事件源
        JButton yellowButton = new JButton("Yellow");
        JButton blueButton = new JButton("Blue");
        JButton redButton = new JButton("Red");

        //buttonPanel 面板
        buttonPanel = new JPanel();

        buttonPanel.add(yellowButton);
        buttonPanel.add(blueButton);
        buttonPanel.add(redButton);

        add(buttonPanel);
        // 监听器
        ColorAction yellowAction = new ColorAction(Color.YELLOW);
        ColorAction blueAction = new ColorAction(Color.BLUE);
        ColorAction redAction = new ColorAction(Color.RED);

        //事件源能够添加监听器
        yellowButton.addActionListener(yellowAction);
        blueButton.addActionListener(blueAction);
        redButton.addActionListener(redAction);
    }

    private class ColorAction implements ActionListener {
        private Color backgroundColor;

        public ColorAction(Color c) {
            this.backgroundColor = c;
        }
        @Override
        public void actionPerformed(ActionEvent e) {
            buttonPanel.setBackground(backgroundColor);
        }
    }
    public static void main(String[] args) {
        ButtonFrame buttonFrame = new ButtonFrame();
        buttonFrame.setVisible(true);
        // 当关闭窗体时,退出程序。
        buttonFrame.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                super.windowClosing(e);
                System.exit(0);
            }
        });
    }
}

事件机制在Spring的运用

Spring 事件发布的核心类 AbstractApplicationContext

事件最终还是由组播(ApplicationEventMulticaster)发布。

Spring 事件基类 ApplicationEvent

  • 相对于 java.util.EventObject 增加事件发生时间戳 timestamp

Spring 事件监听器 ApplicationListener

  • 事件监听器接口,事件的业务逻辑封装在监听器里面。

  • ApplicationEventPublisherAware 事件发送器,通过实现这个接口,来触发事件。

    • ApplicationEventPublisherAware 带有Aware 标记接口,在spring中调用 referesh()方法中的prepareBeanFactory(beanFactory); 中ApplicationEventPublisher 等组件会被自动装配在spring容器中。在通过IOC容器使用ApplicationEventPublisherAware 实现类可以 通过成员属性获取实例。

      @Component
      public class TestPublish implements ApplicationEventPublisherAware {
          
          private static ApplicationEventPublisher applicationEventPublisher;
          // 这个方法可以从IOC容器中拿到ApplicationEventPublisher实例。
          @Override
          public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
              TestPublish.applicationEventPublisher = applicationEventPublisher;
          }
          // 发布事件
          public static void  publishEvent(ApplicationEvent communityArticleEvent) {
              applicationEventPublisher.publishEvent(communityArticleEvent);
          }
      }
      
  • ApplicationEventMulticaster 事件广播器

    public class ApplicationEventMulticasterDemo {
    
        public static void main(String[] args) {
            ApplicationEventMulticaster multicaster = new SimpleApplicationEventMulticaster();
    
            // 添加监听器
            multicaster.addApplicationListener(event -> {
                if (event instanceof PayloadApplicationEvent) {
                    System.out.println("接受到 PayloadApplicationEvent :"
                            + PayloadApplicationEvent.class.cast(event).getPayload());
                }else {
                    System.out.println("接收到事件:" + event);
                }
            });
            // 发布/广播事件
            multicaster.multicastEvent(new MyEvent("Hello,World"));
            multicaster.multicastEvent(new PayloadApplicationEvent<Object>("2", "Hello,World"));
        }
        private static class MyEvent extends ApplicationEvent {
            public MyEvent(Object source) {
                super(source);
            }
        }
    }
    
  • GenericApplicationContext 通过IOC容器事件发布

    • context.publishEvent(Object event) 注意参数(可以任意一个对象)。析源码可知。
    public class SpringEventListenerDemo {
    
        public static void main(String[] args) {
            GenericApplicationContext context = new GenericApplicationContext();
    
            // 添加事件监听器
    //        context.addApplicationListener(new ApplicationListener<ApplicationEvent>() {
    //            @Override
    //            public void onApplicationEvent(ApplicationEvent event) {
    //                System.err.println("监听事件:" + event);
    //
    //            }
    //        });
    
            // 添加自义定监听器 --》监听容器启动过程
            context.addApplicationListener(new ClosedListener());
            context.addApplicationListener(new RefreshedListener());
            // 启动 Spring 应用上下文
            context.refresh();
    
            // 一个是 ContextRefreshedEvent
            // 一个是 PayloadApplicationEvent
            // Spring 应用上下文发布事件
            context.publishEvent("HelloWorld"); // 发布一个 HelloWorld 内容的事件
            // 一个是 MyEvent
            context.publishEvent(new MyEvent("HelloWorld 2018"));
    
            // 一个是 ContextClosedEvent
            // 关闭应用上下文
            context.close();
        }
    
        private static class RefreshedListener implements ApplicationListener<ContextRefreshedEvent> {
    
            @Override
            public void onApplicationEvent(ContextRefreshedEvent event) {
                System.out.println("上下文启动:" + event);
            }
        }
    
        private static class ClosedListener implements ApplicationListener<ContextClosedEvent> {
            @Override
            public void onApplicationEvent(ContextClosedEvent event) {
                System.out.println("关闭上下文:" + event);
            }
        }
    
        private static class MyEvent extends ApplicationEvent {
            public MyEvent(Object source) {
                super(source);
            }
        }
    }
    
  • 源码分析 context.publishEvent 发布事件

    public abstract class AbstractApplicationContext extends DefaultResourceLoader
    		implements ConfigurableApplicationContext {
    		
    		....
    // 核心方法		
    protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
    		Assert.notNull(event, "Event must not be null");
    		if (logger.isTraceEnabled()) {
    			logger.trace("Publishing event in " + getDisplayName() + ": " + event);
    		}
    
    		// 如有必要,将事件装饰为ApplicationEvent
    		ApplicationEvent applicationEvent;
    		if (event instanceof ApplicationEvent) {
    			applicationEvent = (ApplicationEvent) event;
    		}
    		// 如果不是ApplicationEvent 默认为PayloadApplicationEvent事件发布。
    		else {
    			applicationEvent = new PayloadApplicationEvent<>(this, event);
    			if (eventType == null) {
    				eventType = ((PayloadApplicationEvent) applicationEvent).getResolvableType();
    			}
    		}
    
    		// 将时间事件加入earlyApplicationEvents 容器
    		if (this.earlyApplicationEvents != null) {
    			this.earlyApplicationEvents.add(applicationEvent);
    		}
    		// 通过ApplicationEventMulticaster 开始组播。事件最终还是由组播发送。
    		else {
    			getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
    		}
    
    		// 通过父类上下文发布事件。
    		if (this.parent != null) {
    			if (this.parent instanceof AbstractApplicationContext) {
    				((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
    			}
    			else {
    				this.parent.publishEvent(event);
    			}
    		}
    	}
    	
    	.....
    }
    
  • 源码分析ApplicationEventMulticaster#multicastEvent(ApplicationEvent, ResolvableType)

    // 该类是ApplicationEventMulticaster 一个实现,说明事件不一定是单线程执行。
    public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
    
    	@Nullable
    	private Executor taskExecutor;
        ...
        
        @Override
    	public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
    		ResolvableType type = (eventType != null ? 
                                   eventType : resolveDefaultEventType(event));
            // 判断是否能够获取 Executor
    		 Executor executor = getTaskExecutor();
    		for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
                // 如果执行器不为空,就使用多线程执行
    			if (executor != null) {
    				executor.execute(() -> invokeListener(listener, event));
    			}
    			else {
                    // 否则使用单线程执行
    				invokeListener(listener, event);
    			}
    		}
    	}
        
        ...    
            
    }
    
  • 上下文容器监听事件

    // springboot 内容
    @EnableAutoConfiguration
    public class SpringBootEventDemo {
    
        public static void main(String[] args) {
            new SpringApplicationBuilder(SpringBootEventDemo.class)
                    .listeners(event -> { // 增加监听器
                        System.err.println("监听到事件 : " + event.getClass().getSimpleName());
                    })
                    .run(args)
                    .close();
            ; // 运行
        }
    }
    
  • ssm 框架整合中 在web.xml 中通过Tomcat在启动时触发ContextLoaderListener 启动容器。

    <!-- Spring监听器 -->
        <listener>
            <listener-class>
                  org.springframework.web.context.ContextLoaderListener
            </listener-class>
        </listener>
    
    public void contextInitialized(ServletContextEvent event) {
       this.initWebApplicationContext(event.getServletContext()); // 启动容器
    }
    
  • 容器调用 事件流程

    refresh() -> finishRefresh() -> publishEvent(new ContextRefreshedEvent(this));

  • 发送 Spring 事件通过

    ApplicationEventMulticaster#multicastEvent(ApplicationEvent, ResolvableType)

  • Spring 事件的类型 ApplicationEvent

    Spring 事件监听器 ApplicationListener

    Spring 事件广播器 ApplicationEventMulticaster

    • 实现类:SimpleApplicationEventMulticaster
  • 自定义事件都是PayloadApplicationEvent

  • 事件不一定是单线程执行。

关注我,一起学习Java.
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值