xinrong2019 / xinrong2019.github.io

My Blog
https://xinrong2019.github.io
1 stars 1 forks source link

20190721 Spring Framework体系化学习之IOC容器(最后内容) #93

Open xinrong2019 opened 5 years ago

xinrong2019 commented 5 years ago

1.13. Environment Abstraction

Environment接口是集成在容器中的抽象,它模拟了应用程序环境的两个关键方面:配置文件属性

配置文件是仅在给定配置文件处于活动状态时才向容器注册的bean定义的命名逻辑组。可以将Bean分配给配置文件,无论是以XML还是使用注释定义。与配置文件相关的Environment对象的作用是确定哪些配置文件(如果有)当前处于活动状态,以及默认情况下哪些配置文件(如果有)应处于活动状态。

属性在几乎所有应用程序中都发挥着重要作用,可能源自各种源:属性文件,JVM系统属性,系统环境变量,JNDI,servlet上下文参数,ad-hoc属性对象,Map对象等。与属性相关的Environment对象的作用是为用户提供方便的服务接口,用于配置属性源和从中解析属性。

1.13.1. Bean Definition Profiles

1.13.2. PropertySource Abstraction

1.13.3. Using @PropertySource

1.13.4. Placeholder Resolution in Statements

xinrong2019 commented 5 years ago

1.14. Registering a LoadTimeWeaver

1.15. Additional Capabilities of the ApplicationContext

正如章节介绍中所讨论的,org.springframework.beans.factory包提供了管理和操作bean的基本功能,包括以编程方式。 org.springframework.context包添加了ApplicationContext接口,该接口扩展了BeanFactory接口,此外还扩展了其他接口,以更面向应用程序框架的方式提供其他功能。许多人以完全声明的方式使用ApplicationContext,甚至不以编程方式创建它,而是依赖于诸如ContextLoader之类的支持类来自动实例化ApplicationContext,作为Java EE Web应用程序的正常启动过程的一部分。

为了以更加面向框架的样式增强BeanFactory功能,上下文包还提供以下功能:

1.15.1. Internationalization using MessageSource

1.15.2. Standard and Custom Events

ApplicationContext中的事件处理是通过ApplicationEvent类和ApplicationListener接口提供的。如果将实现ApplicationListener接口的bean部署到上下文中,则每次将ApplicationEvent发布到ApplicationContext时,都会通知该bean。从本质上讲,这是标准的Observer设计模式。

从Spring 4.2开始,事件基础结构得到了显着改进,并提供了基于注释的模型以及发布任意事件的能力(即,不一定从ApplicationEvent扩展的对象)。当发布这样的对象时,我们将它包装在一个事件中。

The following table describes the standard events that Spring provides:

Built-in Events

Event Explanation
ContextRefreshedEvent 初始化或刷新ApplicationContext时发布(例如,通过使用ConfigurableApplicationContext接口上的refresh()方法)。这里,“初始化”意味着加载所有bean,检测并激活后处理器bean,预先实例化单例,并且ApplicationContext对象已准备好使用。只要上下文尚未关闭,只要所选的ApplicationContext实际支持这种“热”刷新,就可以多次触发刷新。例如,XmlWebApplicationContext支持热刷新,但GenericApplicationContext不支持。
ContextStartedEvent 通过使用ConfigurableApplicationContext接口上的start()方法启动ApplicationContext时发布。这里,“已启动”表示所有生命周期bean都会收到明确的启动信号。通常,此信号用于在显式停止后重新启动Bean,但它也可用于启动尚未为自动启动配置的组件(例如,尚未在初始化时启动的组件)。
ContextStoppedEvent 通过使用ConfigurableApplicationContext接口上的stop()方法停止ApplicationContext时发布。这里,“已停止”表示所有生命周期bean都会收到明确的停止信号。可以通过start()调用重新启动已停止的上下文。
ContextClosedEvent 通过使用ConfigurableApplicationContext接口上的close()方法关闭ApplicationContext时发布。这里,“关闭”意味着所有单例bean都被销毁。封闭的环境达到了生命的终点。它无法刷新或重新启动。
RequestHandledEvent 一个特定于Web的事件,告诉所有bean已经为HTTP请求提供服务。请求完成后发布此事件。此事件仅适用于使用Spring的DispatcherServlet的Web应用程序。

您还可以创建和发布自己的自定义事件。以下示例显示了一个扩展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;
    }

    // accessor and other methods...
}

要发布自定义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()。实际上,传入的参数是Spring容器本身。您正在通过其ApplicationEventPublisher接口与应用程序上下文进行交互。

要接收自定义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通常使用自定义事件的类型进行参数化(前面示例中为BlackListEvent)。这意味着onApplicationEvent()方法可以保持类型安全,从而避免任何向下转换的需要。您可以根据需要注册任意数量的事件侦听器,但请注意,默认情况下,事件侦听器会同步接收事件。这意味着publishEvent()方法将阻塞,直到所有侦听器都已完成对事件的处理。这种同步和单线程方法的一个优点是,当侦听器接收到事件时,如果事务上下文可用,它将在发布者的事务上下文内运行。如果需要另一个事件发布策略,请参阅Spring的ApplicationEventMulticaster接口的javadoc。

以下示例显示了用于注册和配置上述每个类的bean定义:


<bean id="emailService" class="example.EmailService">
    <property name="blackList">
        <list>
            <value>known.spammer@example.org</value>
            <value>known.hacker@example.org</value>
            <value>john.doe@example.org</value>
        </list>
    </property>
</bean>

<bean id="blackListNotifier" class="example.BlackListNotifier">
    <property name="notificationAddress" value="blacklist@example.org"/>
</bean>

总而言之,当调用emailService bean的sendEmail()方法时,如果有任何应列入黑名单的电子邮件消息,则会发布BlackListEvent类型的自定义事件。 blackListNotifier bean注册为ApplicationListener并接收BlackListEvent,此时它可以通知相关方。

Spring的事件机制是为在同一应用程序上下文中的Spring bean之间的简单通信而设计的。但是,对于更复杂的企业集成需求,单独维护的Spring Integration项目为构建基于众所周知的Spring编程模型的轻量级,面向模式,事件驱动的体系结构提供了完整的支持。

Annotation-based Event Listeners

从Spring 4.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() {
    ...
}

还可以通过使用定义SpEL表达式的注释的condition属性来添加额外的运行时过滤,该表达式应匹配以实际调用特定事件的方法。

以下示例显示了仅当事件的content属性等于my-event时,才能重写我们的通知程序以进行调用:

@EventListener(condition = "#blEvent.content == 'my-event'")
public void processBlackListEvent(BlackListEvent blEvent) {
    // notify appropriate parties via notificationAddress...
}

每个SpEL表达式都针对专用上下文进行评估。下表列出了可用于上下文的项目,以便您可以将它们用于条件事件处理:

表格看这里

请注意,即使您的方法签名实际引用已发布的任意对象,#root.event也允许您访问基础事件。

如果您需要作为处理其他事件的结果发布事件,则可以更改方法签名以返回应发布的事件,如以下示例所示:

@EventListener
public ListUpdateEvent handleBlackListEvent(BlackListEvent event) {
    // notify appropriate parties via notificationAddress and
    // then publish a ListUpdateEvent...
}

This feature is not supported for asynchronous listeners.

这个新方法为上面方法处理的每个BlackListEvent发布一个新的ListUpdateEvent。如果需要发布多个事件,则可以返回事件集合。

Asynchronous Listeners

Ordering Listeners

Generic Events

1.15.3. Convenient Access to Low-level Resources

1.15.4. Convenient ApplicationContext Instantiation for Web Applications

1.15.5. Deploying a Spring ApplicationContext as a Java EE RAR File

1.16. The BeanFactory

BeanFactory API为Spring的IoC功能提供了基础。它的特定契约主要用于与Spring的其他部分和相关的第三方框架集成,其DefaultListableBeanFactory实现是更高级别GenericApplicationContext容器中的密钥委托。

BeanFactory和相关接口(例如BeanFactoryAware,InitializingBean,DisposableBean)是其他框架组件的重要集成点。通过不需要任何注释或甚至反射,它们允许容器与其组件之间的非常有效的交互。应用程序级bean可以使用相同的回调接口,但通常更喜欢通过注释或通过编程配置进行声明性依赖注入。

请注意,核心BeanFactory API级别及其DefaultListableBeanFactory实现不会对配置格式或要使用的任何组件注释做出假设。所有这些风格都通过扩展(例如XmlBeanDefinitionReader和AutowiredAnnotationBeanPostProcessor)进行,并作为核心元数据表示在共享BeanDefinition对象上运行。这是使Spring的容器如此灵活和可扩展的本质。

1.16.1. BeanFactory or ApplicationContext?