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

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.
在这里插入图片描述

<think>好的,我现在需要帮助用户解决关于Dify循环实现的问题。首先,我需要明确用户的具体需求。他们提到“dify 循环实现”或“dify 如何处理循环”,但Dify本身可能没有直接称为“循环”的功能,所以需要结合现有文档中的信息来推断可能的解决方案。 根据用户提供的引用内容,Dify工作流中有迭代节点[^1]。这可能类似于编程中的循环结构,用于重复执行某个任务。例如,在处理列表数据或需要多次执行同一操作时,迭代节点可以派上用场。我需要详细解释迭代节点的配置和使用步骤,确保用户能够理解如何设置循环次数或遍历数据集。 另外,用户可能对如何处理循环中的错误或性能问题感兴趣。引用[2]提到在Dify中处理PEM文件的问题,这可能涉及在循环中正确处理数据格式,避免错误积累。需要提醒用户在循环中注意错误处理和资源管理,确保每次迭代的稳定性。 引用[3]中的例子展示了如何结合FastAPI和Selenium进行实时监控并生成GIF,这可能涉及循环截取浏览器内容。虽然Dify本身可能不直接控制这类循环,但可以通过外部脚本或定时任务触发工作流,模拟循环执行。这部分需要说明如何将外部循环机制与Dify结合使用。 接下来,我需要组织回答结构。首先介绍迭代节点的基本概念和配置步骤,然后讨论循环中的数据处理和错误处理,最后补充外部触发循环的方法。同时,生成相关问题以帮助用户进一步探索,比如优化循环性能或处理复杂数据结构。 需要确保回答符合格式要求,使用正确的中文表达,行内公式用$...$,独立公式用$$...$$,但当前问题不涉及数学公式,所以可能不需要使用。同时,引用标识要自然地添加到相关段落末尾,如、[^2]、[^3]。 最后,检查是否有遗漏点,比如Dify的其他循环相关功能或用户可能遇到的常见问题,确保回答全面且易于理解。</think>在Dify中处理循环逻辑主要通过**迭代节点**实现,以下是具体实现方式和应用场景的解析: ### 一、Dify循环实现机制 Dify通过**工作流设计器**中的迭代节点处理循环需求,其核心原理类似编程中的`for循环`。迭代节点可遍历以下数据类型: - 数组列表:`["A","B","C"]` - 字典集合:`{"key1":"value1", "key2":"value2"}` - 数值范围:通过`range()`函数生成序列 配置示例: ```python # 模拟迭代节点的数据输入 input_data = { "dataset": [1,2,3,4,5], "process_logic": "item * 2" # 对每个元素执行乘以2的操作 } ``` ### 二、迭代节点的关键配置步骤 1. **数据源绑定**:将数组/字典类型变量连接到迭代节点的输入端口 2. **循环变量命名**:设定当前元素的变量名(默认为`item`) 3. **子流程设计**:在迭代节点内部构建需要重复执行的逻辑模块 4. **结果聚合**:通过`outputs`收集所有迭代结果,支持数组或对象格式 $$ \text{总耗时} = \sum_{i=1}^{n}(单次迭代时间_i) + 系统开销 $$ ### 三、循环中的特殊处理 1. **错误中断控制**: - 启用`continueOnError`参数可跳过失败迭代 - 通过`try-catch`模块包裹敏感操作 2. **并行优化**: ```python # 伪代码示例 Parallel.forEach(dataset, lambda item: process(item)) ``` 3. **结果过滤**: ```python filtered = filter(lambda x: x%2==0, processed_results) ``` ### 四、应用场景案例 1. **批量文件处理**:遍历存储桶中的文件列表进行格式转换 2. **数据清洗**:对数据库查询结果集进行逐条校验 3. **API轮询**:定时循环调用第三方接口直到满足特定条件
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值