Spring 中时事件处理机制——ApplicationEvent

ApplicationContext中的事件处理通过ApplicationEvent类和ApplicationListener接口提供。如果实现ApplicationListener接口的bean注册到ApplicationContext中,则每次ApplicationEvent发布到ApplicationContext时,都会通知该bean。本质上,这是标准的观察者设计模式。从Spring4.2开始,事件结构得到了显著的改进,提供了一个基于注解的模型以及发布任意事件的能力(也就是说,不一定从ApplicationEvent扩展的对象)。当这样一个对象被发布时,我们将它包装在一个事件中。了解Spring的事件机制,对于Spring与其他第三方框架的集成一级Spring的扩展有很大的帮助。在Dubbo就是通过Spring的事件机制发布服务的,其中Spring和也提供了很多内置的事件,如下表所示:

事件

说明

ContextRefreshedEvent

在初始化或刷新ApplicationContext时发布(例如通过在ConfigurableApplicationContext接口上使用refresh()方法)。这里,“初始化”意味着加载所有bean,检测并激活post-processor bean,预先实例化单例,并且ApplicationContext对象准备好使用。只要上下文未关闭,只要所选的ApplicationContext实际上支持此类“热”刷新,就可以多次触发刷新。例如,XMLWebApplicationContext支持热刷新,但GenericApplicationContext不支持。

ContextStartedEvent

在可配置的ConfigurableApplicationContext 接口上使用start()方法启动ApplicationContext时发布。这里,“启动”意味着所有生命周期bean都会收到一个明确的启动信号。此信号用于在明确停止后重新启动be an,但也可以用于启动尚未配置为自动启动的组件(例如,初始化时尚未启动的组件)。

ContextStoppedEvent

在ConfigurableApplicationContext的接口上使用stop()方法停止ApplicationContext时发布。这里,“停止”意味着所有生命周期bean都会收到一个明确的停止信号。停止的上下文可以通过start()调用重新启动。

ContextClosedEvent

在ConfigurableApplicationContext接口上使用close()方法关闭ApplicationContext时发布。这里,“关闭”意味着所有的单例bean都被销毁了。封闭的环境达到了生命的尽头。无法刷新或重新启动。

RequestHandledEvent

一个特定的web事件告诉所有beans,HTTP请求已经被处理。这一事件在请求完成之后发布。该事件仅仅适用于适用Spring DispatcherServlet的web 应用

我们也可以通过 ApplicationEvent类和ApplicationListener接口创建自己的事件,下面的示例显示了一个扩展Spring的ApplicationEvent基类的简单类:

public class BlackListEvent extends ApplicationEvent {
    private final String address;
    private final String content;

    public BlackListEvent(Object source, String address, String content) {
        super(source);
        this.address = address;
        this.content = content;
    }
    // 其他方法
}

若要发布自定义ApplicationEvent,需要在ApplicationEventPublisher上调用PublishEvent()方法。通常,通过创建一个实现ApplicationEventPublisheraware的类并将其注册为Spring Bean来完成的。如下示例所示:

public class EmailService implements ApplicationEventPublisherAware {

    private List<String> blackList;
    private ApplicationEventPublisher publisher;

    public void setBlackList(List<String> blackList) {
        this.blackList = blackList;
    }

    public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
        this.publisher = publisher;
    }

    public void sendEmail(String address, String content) {
        if (blackList.contains(address)) {
            publisher.publishEvent(new BlackListEvent(this, address, content));
            return;
        }
        // send email...
    }
}

在配置时,Spring容器检测到EmailService实现了ApplicationEventPublisheraware,并自动调用setApplicationEventPublisher()注入ApplicationEventPublisher 实例。实际上,传入的参数是Spring容器本身。要接收自定义的ApplicationEvent,可以创建一个实现ApplicationListener 的类,并将其注册为Spring Bean。代码如下所示:

public class BlackListNotifier implements ApplicationListener<BlackListEvent> {
    private String notificationAddress;
    public void setNotificationAddress(String notificationAddress) {
        this.notificationAddress = notificationAddress;
    }
    public void onApplicationEvent(BlackListEvent event) {
        // notify appropriate parties via notificationAddress...
    }
}

注意,ApplicationListener通常是用自定义事件的类型(前面的示例中是BackListEvent)参数化。这意味着onApplicationEvent()方法可以保证类型安全,避免任何向下强制转换的需要。您可以注册任意多个事件侦听器,但请注意,默认情况下,事件侦听器同步接收事件。这意味着publishEvent()方法将一直阻塞,直到所有侦听器完成对事件的处理。这种同步和单线程方法的一个优点是,当侦听器接收到事件时,如果事务上下文可用,它将在发布服务器的事务上下文中操作。如果需要事件发布的另一个策略,请参阅ApplicationEventMulticaster接口。如下为上面示例中的Spring配置:

<bean id="emailService" class="example.EmailService">
    <property name="blackList">
        <list>
            <value>[email protected]</value>
            <value>[email protected]</value>
            <value>[email protected]</value>
        </list>
    </property></bean>
<bean id="blackListNotifier" class="example.BlackListNotifier">
    <property name="notificationAddress" value="[email protected]"/>
</bean>

当调用emailServicebean的sendemail()方法时,如果有任何电子邮件应被列入黑名单,则会发布一个BlackListEvent类型的自定义事件。blackListNotifier bean注册为ApplicationListener并接收BlackListEvent,此时它可以通知相应的方。

除了使用ApplicationEventPublisher发布事件外,我们可以通过ApplicationContext实例发布事件,ApplicationContext提供了publishEvent()方法用于发布事件,代码如下所示:

public class MessageEvent extends ApplicationEvent {
    private static final long serialVersionUID = 1L;
    private String eventType;
    public String getEventType() {
	return eventType;
    }
    public void setEventType(String eventType) {
	this.eventType = eventType;
    }
    public MessageEvent(Object source) {
	super(source);
    }
}
@Component
public class MessageEventListener implements ApplicationListener<MessageEvent> {
    public void onApplicationEvent(MessageEvent event) {
        String eventType = event.getEventType();
	//发布事件的对象
	Object object = event.getSource();
    }
}
public class SpringEventTest {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext("cn.org.microservice.spring.ioc.event");
	MessageEvent event = new MessageEvent(applicationContext);
	event.setEventType("发布");
	applicationContext.publishEvent(event);
    }
}

Spring的事件机制是为同一应用程序上下文中SpringBean之间的简单通信而设计的。然而,对于更复杂的企业集成需求,单独维护的Spring Integration项目为构建基于Spring编程模型的轻量级、面向模式、事件驱动的体系结构提供了完整的支持。从Spring4.2开始,您可以使用EventListener注解在被托管bean的任何公共方法上注册事件侦听器。BlackListNotifier程序可以改写如下:

public class BlackListNotifier {
    private String notificationAddress;
    public void setNotificationAddress(String notificationAddress) {
        this.notificationAddress = notificationAddress;
    }
    @EventListener
    public void processBlackListEvent(BlackListEvent event) {
        // notify appropriate parties via notificationAddress...
    }
}

方法签名再次声明它要侦听的事件类型,但这次使用灵活的名称,而不实现特定的侦听器接口。只要实际事件类型在其实现层次结构中解析泛型参数,就可以通过泛型缩小事件类型。如果您的方法应该监听多个事件,或者您想要定义它而不使用任何参数,那么也可以在注解本身上指定事件类型。以下示例显示了如何执行此操作:

@EventListener({ContextStartedEvent.class, ContextRefreshedEvent.class})
public void handleContextStart() {
    ...
}

如果希望特定的侦听器异步处理事件,可以重用常规的@Async支持。以下示例显示了如何执行此操作:

@EventListener
@Async
public void processBlackListEvent(BlackListEvent event) {
    // BlackListEvent is processed in a separate thread
}

如果需要先调用一个监听器,然后再调用另一个监听器,则可以将@Order注解添加到方法声明中,如下例所示:

@EventListener
@Order(42)
public void processBlackListEvent(BlackListEvent event) {
    // notify appropriate parties via notificationAddress...
}

您还可以使用泛型进一步定义事件的结构。考虑使用EntityCreatedEvent,其中t是创建的实际实体的类型。例如,您可以创建以下侦听器定义以仅接收Person的EntityCreatedEvent:

@EventListener
public void onPersonCreated(EntityCreatedEvent<Person> event) {
    ...
}

由于类型消除,只有在激发的事件解析事件侦听器筛选的泛型参数(即类PersonCreatedEvent扩展EntityCreatedEvent<Person> { …})时,此操作才有效。在某些情况下,如果所有事件都遵循相同的结构(如前一个示例中的事件一样),则这可能会变得相当乏味。在这种情况下,您可以实现可ResolvableTypeProvider,以指导框架在运行时环境之上提供内容。以下事件显示如何执行此操作:

public class EntityCreatedEvent<T> extends ApplicationEvent implements ResolvableTypeProvider {

    public EntityCreatedEvent(T entity) {
        super(entity);
    }

    @Override
    public ResolvableType getResolvableType() {
        return ResolvableType.forClassWithGenerics(getClass(), ResolvableType.forInstance(getSource()));
    }
}

猜你喜欢

转载自blog.csdn.net/wk19920726/article/details/108717994