《Spring揭秘》第五章 读书笔记 —— ApplicationContext

来源:互联网 发布:.com.hk域名注册 编辑:程序博客网 时间:2024/05/18 01:03

在第三章中已经提到了,ApplicationContext除了和BeanFactory共同实现了两个接口,但是ApplicationContext有另外实现了其他三个接口,分别是FileSystemXmlApplicationContext、FileSystemXmlApplicationContext、XmlWebApplicationContext,这也就意味着ApplicationContext在功能上面有了扩展,将其当做是BeanFactory的升级版也是可以的

  1. 统一资源加载策略
    spring框架中使用org.springframework.core.io.Resource作为所有的资源文件访问的接口,有几种不同的使用场景的接口:
    · ByteArrayResource 该实现根据字节数组的数据,构造相应的ByteArrayInputStream并返回
    · ClassPathResource 从Java应用程序的ClassPath中加载具体资源并封装,使用指定的类加载器或者给定的类进行资源加载
    · URLResource 通过java.net.URL进行具体的资源加载
    · InputStreamResource 较为少用的资源加载Resource的实现类,一般可用ByteArrayResource代替
public interface Resource extends InputStreamSource {    boolean exists();    boolean isOpen();    URL getURL() throws IOException;    File getFile() throws IOException;    Resource createRelative(String relativePath) throws IOException;    String getFilename();    String getDescription();    }public interface InputStreamSource {    InputStream getInputStream() throws IOException;}

在Resource中定义了七个方法,可以用来对资源文件的查找、访问

ResourceLoader,“更加广义的URL”

  • DefaultResourceLoader
    ResourceLoader的默认的资源加载机制,默认加载逻辑:

(1) 首先查询资源路径是否是以“classpath:”开头,如果是,就尝试构造ClassPathLoader类型资源返回
(2) 否则,常使用URL定位资源文件位置,如果没有抛出MalformedURLException,就会构造URLResource类型的资源并返回;如果无法找到资源文件,则需要委派getResourceByPath(String xx)方法来定位

  • FileSystemResourceLoader
    为了避免DefaultResourceLoader在getResourceByPath(String xx)不恰当的处理,FileSystemResourceLoader继承了DefaultResourceLoader并重写了getResourceByPath(String xx)方法,返回FileSystemResourceLoader类型资源,重写方法:
public void testResourceTypesWithFileSystemResourceLoader(){    ResourceLoader resourceLoader = new FileSystemResourceLoader();    Resource fileResource = resourceLoader.getResource("D:/spring21site/README");    assertTrue(fileResource instanceof FileSystemResource);    assertTrue(fileResource.exists());    Resource urlResource = resourceLoader.getResource("file:D:/spring21site/README");    assertTrue(urlResource instanceof UrlResource); }

覆写getResourceByPath(String xx)方法的逻辑,以改变DefaultResourceLoader的默认资源加载行为,最终从文件系统中加载并返回FileSystemResource类型的资源。

ResourcePatternResolver——批量查找的ResourceLoader
ResourceLoader是加载一个资源文件,返回一个资源实例,而ResourcePatternResolver是可以批量查找资源文件,返回多个资源实例,ResourcePatternResolver的接口定义:

public interface ResourcePatternResolver extends ResourceLoader {    String CLASSPATH_ALL_URL_PREFIX = "classpath*:";    Resource[] getResources(String locationPattern) throws IOException;}

从接口定义中可以看出,ResourcePatternResolver引入了 Resource[] getResources(String locationPattern),也就是说匹配过程中将资源文件都存入这个数组中,返回的是资源文件夹,并且有了classpath*:的新定义。但是加载资源文件的方式是一致的

各个接口之间的关系
这里写图片描述

  • ApplicationContext与ResourceLoader
    所有的ApplicationContext实现类会直接或者间接地继承AbstractApplicationContext,AbstractApplicationContext继承了DefaultResourceLoader,所以说ApplicationContext的实现类可以直接查找加载资源文件
    • 可以通过ApplicationContext的实现类加载Spring支持的Resource类型
    • 如果某个bean需要依赖特定的ResourceLoader查找资源,可以为其注入ResourceLoader的具体实现

  1. 国际化信息支持
    JavaSE对于国际化有了很好的支持,涉及的两个类是Local和ResourceBundle,都是util包中的类
    (1)Local用于代表不同的国家和地区,例如:Local.CHINA代表中国
    (2)ResourceBundle用来保存特定于某个Locale的信息(可以是String类型信息,也可以是任何类型的对象),相应的有properties文件,我们就可以通过ResourceBundle的getBundle(String baseName, Locale locale)方法取得不同Locale对应的ResourceBundle,再从ResourceBundle获取信息,对于MessageSource提供了三种方法:
public interface MessageSource {    String getMessage(String code, Object[] args, String defaultMessage, Locale locale);    String getMessage(String code, Object[] args, Locale locale) throws NoSuchMessageException;    String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessage zException;}

第二种方法和第一个方法区别在于找不到资源的话会抛出异常
ApplicationContext也实现了MessageSource接口,它会委派MessageSource接口来完成MessageSource的工作,如果不存在这个接口,容器会默认创建一个不含任何内容的StaticMessageSource实例,完成相应的工作
这里写图片描述
上图是spring实现Message的三个接口


  1. 容器内部事件发布
    自定义事件类型,通过扩展java.util.EventObject类来实现自定义事件类型,实现针对自定义事件类的事件监听接口,对自定义时间进行监听
public interface MethodExecutionEventListener extends EventListener {    /**    * 处理方法开始执行的时候发布的MethodExecutionEvent事件    */    void onMethodBegin(MethodExecutionEvent evt);    /** * 处理方法执行将结束时候发布的MethodExecutionEvent事件    */    void onMethodEnd(MethodExecutionEvent evt);}

自定义事件事项上面的MethodExecutionEventListener接口,针对不同的事件作出相应的处理
以上为准备工作,有了自定义事件和事件监听器,那么就剩下发布事件,事件发布类需要注意的两点是:

public class MethodExeuctionEventPublisher {    private List<MethodExecutionEventListener> listeners = new     ArrayList<MethodExecutionEventListener>();    public void methodToMonitor()     {        MethodExecutionEvent event2Publish =         new MethodExecutionEvent(this,"methodToMonitor");         publishEvent(MethodExecutionStatus.BEGIN,event2Publish);        // 执行实际的方法逻辑        // ...        publishEvent(MethodExecutionStatus.END,event2Publish);    }    protected void publishEvent(MethodExecutionStatus status, MethodExecutionEvent methodExecutionEvent) {        List<MethodExecutionEventListener> copyListeners =         new ArrayList<MethodExecutionEventListener>(listeners);        for(MethodExecutionEventListener listener:copyListeners)        {            if(MethodExecutionStatus.BEGIN.equals(status))                listener.onMethodBegin(methodExecutionEvent);            else                listener.onMethodEnd(methodExecutionEvent);        }    }    public void addMethodExecutionEventListener(MethodExecutionEventListener listener)    {        this.listeners.add(listener);    }    public void removeListener(MethodExecutionEventListener listener)    {        if(this.listeners.contains(listener))        this.listeners.remove(listener);    }    public void removeAllListeners()    {        this.listeners.clear();    }}

① 具体时点上的事件发布,methodToMonitor()是事件发布的源头,而且对于整个监听事件做了安全复制(safe-copy),防止在事件注册或者删除的时候影响处理过程
② MethodExeuctionEventPublisher提供了事件监听器的添加和移除操作,防止监听器被MethodExeuctionEventPublisher类无限调用,没有remove方法,即使不在使用这些监听器也会依然在MethodExeuctionEventPublisher的监听器列表中,造成内存泄漏
自定义事件发布类结构图

知道了监听事件的工作流程,那么spring的内部事件监听发布的原理已经渐渐浮现:

  • ApplicationEvent 自定义监听事件接口,有三个实现类:
    • ContextClosedEvent 用于容器即将关闭的时候发布的事件类型
    • ContextRefreshedEvent 容器刷新或者初始化时候发布的事件类型
    • ContextHandleEvent Web请求处理后发布的事件类型,并且其子类类ServletRequestHandledEvent专门用于JavaEE的Servlet请求事件
  • ApplicationListener 自定义事件监听器接口
  • ApplicationContext 容器本身就是事件发布者,但是真正的事件发布并非容器本身,而是委派给org.springframework.context.event.ApplicationEventMulticaster接口

由于ApplicationContext容器将监听委托给其他对象,所以一旦启动容器就会查找名称为ApplicationEventMulticaster的对象实例applicationEventMulticaster,如果不存在此对象,就会默认创建一个SimpleApplicationEventMulticaster来完成任务

这里写图片描述
Spring容器监听事件内部实现结构图

  • Spring容器内事件发布的应用
    Spring中的ApplicationContext容器内的事件发布机制,主要用于单一容器内的简单消息通知和处理,不适合分布式、多进程、多容器之间的事件通知。

  1. 多配置模块加载的简化
    ApplicationContext容器比BeanFactory做的更加好,并非特有的功能
    引入配置文件中BeanFactory就需要创建FileSystemResource的对象进行文件获取,步骤很繁琐,代码量也很大,但是换做是ApplicationContext就比较简单

书中对于这些知识介绍的比较详细,见P.110

0 0
原创粉丝点击