栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Spring学习笔记10

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Spring学习笔记10

文章参考来源:Spring framework官方文档
前言:
org.springframework.beans.factory包提供了管理和操作bean的基本功能,包括以编程的方式
org.springframework.context包添加了ApplicationContext接口,它扩展了BeanFactory接口,此外还扩展了其他接口,以一种更面向应用程序框架的风格提供额外的功能。
许多人以一种完全声明的方式使用ApplicationContext,甚至不是通过编程来创建它,而是依赖于像ContextLoader这样的支持类来自动实例化ApplicationContext,作为Java EE web应用程序正常启动过程的一部分。
为了以更面向框架的方式增强BeanFactory功能,上下文包还提供了以下功能:
(1)通过MessageSource接口以i18n风格访问消息。
(2)通过ResourceLoader接口访问资源,例如url和文件。
(3)事件发布,即通过使用ApplicationEventPublisher接口发布到实现ApplicationListener接口的bean。
(4)通过HierarchicalBeanFactory接口加载多个(分层)上下文,让每个上下文都集中在一个特定的层上,比如应用程序的web层。

ApplicationContext源码如下:

package org.springframework.context;

import org.springframework.beans.factory.HierarchicalBeanFactory;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.core.env.EnvironmentCapable;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.lang.Nullable;

public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, 
HierarchicalBeanFactory, MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
    @Nullable
    String getId();

    String getApplicationName();

    String getDisplayName();

    long getStartupDate();

    @Nullable
    ApplicationContext getParent();

    AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;
}
1. 使用MessageSource国际化

从ApplicationContext源码看,ApplicationContext接口继承了一个名为MessageSource的接口,因此提供了国际化(“i18n”)功能。Spring还提供了HierarchicalMessageSource接口,该接口可以按层次结构解析消息。这些接口一起提供了Spring实现消息解析的基础。在这些接口上定义的方法包括:

  • String getMessage(String code, Object[] args, String default, Locale loc):用于从MessageSource检索消息的基本方法。如果没有为指定的区域设置找到消息,则使用默认消息。通过使用标准库提供的MessageFormat功能,传入的任何参数都成为替换值。
  • String getMessage(String code, Object[] args, Locale loc):本质上与前面的方法相同,但有一点不同:不能指定默认消息。如果找不到消息,则抛出NoSuchMessageException。
  • String getMessage(MessageSourceResolvable resolvable, Locale Locale):上述方法中使用的所有属性也被包装在一个名为MessageSourceResolvable的类中,可以与此方法一起使用。

当加载ApplicationContext时,它会自动搜索上下文中定义的MessageSource bean。bean的名称必须是messageSource。如果找到了这样的bean,那么对上述方法的所有调用都将委托给消息源。如果没有找到消息源,ApplicationContext尝试查找包含同名bean的父类。如果是,则使用该bean作为MessageSource。如果ApplicationContext不能找到任何消息源,则实例化一个空的DelegatingMessageSource,以便能够接受对上面定义的方法的调用。

Spring提供了三种MessageSource实现,ResourceBundleMessageSource,ReloadableResourceBundleMessageSource和StaticMessageSource。它们都实现了HierarchicalMessageSource以实现嵌套消息传递。很少使用StaticMessageSource,但它提供了将消息添加到源的编程方式。下面的示例显示ResourceBundleMessageSource:


    
        
            
                format
                exceptions
                windows
            
        
    

以上例子中,假设在类路径中定义了三个资源包,分别是format、exceptions和windows。任何解析消息的请求都以通过ResourceBundle对象解析消息的jdk标准方式处理。为了便于示例,假设上述两个资源包文件的内容如下所示:
format.properties文件

    message=Alligators rock!

exceptions.properties

argument.required=The {0} argument is required.

以下示例显示了运行MessageSource功能的程序。请记住,所有ApplicationContext实现也是MessageSource实现,因此可以转换为MessageSource接口:

public static void main(String[] args) {
    MessageSource resources = new ClassPathXmlApplicationContext("beans.xml");
    String message = resources.getMessage("message", null, "Default", Locale.ENGLISH);
    System.out.println(message);
}

上述程序的结果输出如下:

Alligators rock!
2. 标准和自定义事件

ApplicationContext中的事件处理是通过ApplicationEvent类和ApplicationListener接口提供的。如果实现ApplicationListener接口的bean被部署到上下文中,那么每当ApplicationEvent被发布到ApplicationContext时,就会通知该bean。本质上,这是标准的Observer设计模式。
Spring 4.2起,事件的发布流程有了显著改进,提供了基于注释的模型以及发布任意事件(即不必从ApplicationEvent扩展的对象)的能力。

以下给出了Spring提供的标准事件:

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

当然,也可以创建和发布自己的自定义事件,如下:

public class BlockedListEvent extends ApplicationEvent {

    private final String address;
    private final String content;

    public BlockedListEvent(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 blockedList;
    private ApplicationEventPublisher publisher;

    public void setBlockedList(List blockedList) {
        this.blockedList = blockedList;
    }

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

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

在配置阶段,Spring容器检测到实现了ApplicationEventPublisherAware接口的EmailService,并自动调用其setApplicationEventPublisher()方法。实际上,传入的参数是Spring容器本身。我们是通过ApplicationEventPublisher接口与application context 进行交互。

要接收自定义的ApplicationEvent,可以创建一个实现ApplicationListener的类,并将其注册为Spring bean。如下:

public class BlockedListNotifier implements ApplicationListener {

    private String notificationAddress;

    public void setNotificationAddress(String notificationAddress) {
        this.notificationAddress = notificationAddress;
    }

    public void onApplicationEvent(BlockedListEvent event) {
        // notify appropriate parties via notificationAddress...
    }
}
基于注解的事件监听器

Spring 4.2开始,可以使用@EventListener注解在被容器管理的bean的任何公共方法上注册事件监听器,之前的BlockedListNotifier 可以改成:

public class BlockedListNotifier {

    private String notificationAddress;

    public void setNotificationAddress(String notificationAddress) {
        this.notificationAddress = notificationAddress;
    }

    @EventListener
    public void processBlockedListEvent(BlockedListEvent event) {
        // notify appropriate parties via notificationAddress...
    }
}

如果希望在方法上应该侦听多个事件,或者希望在不使用任何参数的情况下定义它,可这样使用:

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

甚至可以通过SpEL表达式来定义监听条件:

@EventListener(condition = "#blEvent.content == 'my-event'")
public void processBlockedListEvent(BlockedListEvent blEvent) {
    // notify appropriate parties via notificationAddress...
}
异步监听器

如果需要一个特定的监听器异步处理事件,可以重用常规的@Async支持:

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

在使用异步事件监听器时,要注意以下限制:

  • 如果异步事件监听器抛出异常,它不会传播到调用者。有关更多细节,请参阅 AsyncUncaughtExceptionHandler。
  • 异步事件监听器方法不能通过返回值来发布后续事件。
  • 如果您需要发布另一个事件作为处理的结果,注入ApplicationEventPublisher来手动发布事件。
有序监听器

可以先调用一个监听器,在方法声明中添加@Order注释:

@EventListener
@Order(42)
public void processBlockedListEvent(BlockedListEvent event) {
    // notify appropriate parties via notificationAddress...
}
通用事件监听器

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

@EventListener
public void onPersonCreated(EntityCreatedEvent event) {
    // ...
}

在某些情况下,如果所有事件都遵循相同的结构(前面示例中的事件也应该如此),那么这可能会变得非常冗长乏味。
在这种情况下,可以实现ResolvableTypeProvider来指导运行时环境所提供的框架:

public class EntityCreatedEvent extends ApplicationEvent implements ResolvableTypeProvider {

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

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

这不仅适用于ApplicationEvent,也适用于任何作为事件发送的对象。

3. 便利地访问底层资源

应用程序 context 是一个ResourceLoader,可用于加载Resource资源对象。Resource实质上是JDK java.net.URL类的功能更丰富版本。事实上,Resource的实现在适当的时候包装了一个java.net.URL的实例。
Resource可以以透明的方式从几乎任何位置获取底层资源,包括类路径、文件系统位置、可用标准URL描述的任何位置,以及其他一些变体。如果resource位置字符串是没有任何特殊前缀的简单路径,那么这些资源的来源是特定的,并且适合于实际的应用程序context类型。
提供给ApplicationContext构造方法的位置路径或路径实际上是资源字符串,例如,ClassPathXmlApplicationContext将简单的位置路径视为类路径位置。还可以使用带有特殊前缀的位置路径(资源字符串)来强制从类路径或URL加载定义。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/573105.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号