《Spring揭秘》——IOC梳理3(资源加载,国际化,容器事件,多配置模块加载)

资源访问

JDK提供的访问资源的类并不能很好的满足各类资源访问需求,Spring提供了Resource接口以实现更强大的访问底层资源的能力。

Resource具体实现类:

ByteArrayResource :二进制数组表示的资源,二进制数组资源可以在内存中通过持续构造;

ClassPathResource :类路径下的资源,资源以相对于类路径的方式表示;

FileSystemResource :文件系统资源,资源以文件系统路径的方式表示;

UrlResource :封装了java.net.URL,使用户能够访问任何可以通过URL表示的资源;

InputStreamResource :以输入流返回表示的资源;

ServletContextResource:为访问Web容器上下文中的资源而设计的类,负责Web应用根目录的路径加载资源;

Resource res1 = new FileSystemResource("D:/c3/file.txt");
Resource res2 = new ClassPathResource("conf/file.txt");
InputStream ins1 = res1.getInputStream();
InputStream ins2 = res2.getInputStream();

//资源文件默认采用系统编码读取资源内容,若要采用特殊编码格式,则使用EncodeResource对象资源进行编码 EncodedResource encRes = new EncodedResource(res2, "UTF-8");

资源地址表达式:

  • classpath:从类路径加载资源。classpath*则是指若在多个jar包或文件吸引类路径都拥有一个相同的包名,classpath只会在第一个加载的包下查找,而classpath*会扫描所有类路径下出现的该路径。(注意:ClassPathXmlApplicationContext在即使没有指明classpath情况下,默认从classpath中加载bean定义配置文件。而FileSystemXmlApplicationContext默认从文件系统中加载,若增加classpath:前缀会明确指定classpath中加载bean定义的配置文件。
  • file:从文件系统目录加载资源,可采用绝对路径或相对路径;
  • http://  :从Web服务器中加载资源
  • ftp://   :从FTP服务器中装载资源
  • 没有前缀:根据ApplicationContext具体实现类采用对应的类型的Resource

Ant风格资源地址表达式:

  • ?:匹配文件名中的一个字符;
  • *:匹配文件名中的任意个字符;
  • **:匹配多层路径;

资源加载

查找和定位资源可使用ResourceLoader,其getResource方法可以根据一个资源地址加载文件资源(仅支持带资源类型前缀的表达式,不支持Ant风格的资源路径表达式)。

ResourcePatternResolver扩展ResourceLoader接口,其getResource方法支持带资源类型前缀及Ant风格的资源路径表达式。PathMatchingResourcePatternResolver是其标准实现类。

ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resolver.getResources("classpath*:com/abc/**/*.xml");

要注意的是:在构造PathMatchingResourcePatternResolver实例时,若不指定ResourceLoader,其内部会默认构造一个DefaultResourceLoader实例,内部会将匹配后确定的资源路径委派给他的ResourceLoader来查找和定位资源。可以通过传入其他类型的ResourceLoader来替换内部默认使用的DefaultResourceLoader,从而改变其默认行为,如使用FileSystemResourceLoader ,使PathMatchingResourcePatternResolver的行为跟FileSystemResourceLoader一样。

ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(new FileSystemResourceLoader());

 

此外,如果一个类需要使用ResourceLoader,需要进行ResourceLoader注入时,可以让该类实现ResourceLoaderAware或者ApplicationContextAware接口。(具体参照P91)

总结(p89)

ApplicationContext与ResourceLoader

ApplicationContext继承了ResourcePatternResolver,间接实现了ResourceLoader接口。 所以ApplicationContext支持Spring内统一资源加载策略。 

AbstractApplicationContext继承了DefaultResourceLoader,它的getResource(String)直接用DefaultResourceLoader的了。

AbstractApplicationContext类的内部声明有一个resourcePatternResolver,类型是ResourcePatternResolver,对应的实例类型为PathMatchingResourcePatternResolver 。 

这样,整个ApplicationContext的实现类就完全可以支持ResourceLoader或者ResourcePatternResolver接口。

 国际化(i18n)

“国际化信息”也称为“本地化信息”,包括“语言类型”和“国家/地区的类型”,中文(zh)、英语(en)、中国大陆(CN)、中国台湾(TW)、英国(EN)、美国(US)。

表示语言和国家/地区信息的本地化类为java.lang.Locale。创建本地化对象的示例:

Locale locale1 = Locale.getDefault();
Locale locale2 = Locale.CHINA;
Locale locale3 = new Locale("zh", "CN");

 此外,JDK中提供了几个支持本地化的格式化操作工具类:NumberFormat、DateFormat、MessageFormat。

//NumberFormat
Locale locale = Locale.getDefault();
NumberFormat curFmt = NumberFormat.getCurrencyInstance(locale);
System.out.println(curFmt.format(123.4));
		
//DateFormat
DateFormat df = DateFormat.getDateInstance(DateFormat.LONG,locale);
System.out.println(df.format(new Date()));
		
//MessageFormat:提供了占位符字符串的格式化功能
String pattern1 = "hello,{0},{1}";
Object[] argu = {"Joey",new GregorianCalendar().getTime()};
System.out.println(MessageFormat.format(pattern1, argu));  //使用默认本地化对象格式化信息
		
MessageFormat mf = new MessageFormat(pattern1, Locale.US);
System.out.println(mf.format(argu));   //使用指定的本地化对象格式化信息

ResourceBundle

Java提供了用于加载本地化资源文件的类ResourceBoundle,其为加载及访问资源文件提供了便捷的操作,从类路径加载名为resource的本地化资源文件:

ResourceBundle rb1 = ResourceBundle.getBundle("com/test/resource",Locale.CHINA);
ResourceBundle rb2 = ResourceBundle.getBundle("com/test/resource",Locale.US);
System.out.println(rb1.getString("greeting.common"));
System.out.println(rb2.getString("greeting.common"));

关于本地化资源文件:

若资源名为resource,若语言为英文,国家为美国,则对应的本地化资源文件为resource_en_US.properties。中国大陆地区为resource_zh_CN.properties。

如果指定的本地化资源文件不存在,按以下顺序尝试加载其他资源:本地系统默认本地化对象对应的资源(比如想要加拿大的但没有,系统默认为中国,则使用resource_zh_CN.properties)—>默认的资源(resource.properties)。若依然不存在则会抛出MissingResourceException异常。

 

MessageSource

Spring定义了访问国际化信息的MessageSource接口,并提供了几个易用的实现类:

  • ResourceBundleMessageSource (最为常用,用于正式生产,对于参数化信息和非参数化信息的处理进行优化);
  • StaticMessageSource (多用于测试,不应该用于正式的生产环境);
  • ReloadableResourceBundleMessageSource(主要可以定期刷新并检查底层资源文件是否已经改变) ;

ResourceBundleMessageSource

允许用户通过beanName指定一个资源名或通过beanNames指定一组资源名。

<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
  <property name="basenames">
    <list>
	  <value>com/ioc/test</value>
    </list>
  </property>
</bean>

一般使用时,id可以是其他(比如myResource),通过ctx.getBean("myResource")获得MessageSource,紧接着对MessageSource操作即可。但ApplicationContext实现了MessageSource接口。默认情况下,ApplicationContext将委派容器中一个名称为messageSource的MessageSource接口实现来完成MessageSource应该完成的职责,如果找不到则ApplicationContext内部会默认实例化一个不包含任何内容的StaticMessageSource实例。(所以,若直接使用ApplicationContext处理国际化资源时,上面配置文件的id必须为messageSource。

ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
String message = ctx.getMessage("greeting.common", null, Locale.CHINA); 

Object[] params = {...}; //若greeting.common中使用到占位符
String message = ctx.getMessage("greeting.common", params, Locale.CHINA); 

ReloadableResourceBundleMessageSource

与ResourceBundleMessageSource唯一区别就是其定时刷新资源文件,已便在应用程序不重启的情况下感知资源文件的变化,避免重启带来的负面影响。注意:cacheSounds默认值为-1表示永不刷新。

使用ReloadableResourceBundleMessageSource时,应该避免将信息资源文件放到classpath中,因为这无助于ReloadableResourceBundleMessageSource定期加载文件变更。(api文档:Since application servers typically cache all files loaded from the classpath, it is necessary to store resources somewhere else (for example, in the "WEB-INF" directory of a web app).

<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
  <property name="basenames">
    <list>
      <value>com/test/test</value>
    </list>
  </property>
  <property name="cacheSeconds" value="3"></property>
</bean>

MessageSource类层次结构如下图(p98)

 

容器事件 

Java通过EventObject类和EventListener描述事件和监听器,某个组件或框架要建立自己的事件发布和监听机制,一般都通过扩展它们进行定义。

Spring的ApplicationContext容器内部允许以ApplicationEvent的形式发布事件,容器内注册的ApplicationListener类型的bean会被ApplicationContext容器自动识别,来负责监听容器内发布的所有事件。即一旦容器内发布ApplicationEvent及其子类型的时间,注册到容器的ApplicationListener就会对这些事件进行处理。

ApplicationContext容器的事件发布功能全部委托给了ApplicationEventMulticaster(事件广播器,将事件通知给注册表中的事件监听器)来做,所以,容器启动就会检查容器内是否存在名称为applicationEventMulticaster的对象实例。没有则默认初始化一个SimpleApplicationEventMulticaster作为将会使用的ApplicationEventMulticaster

 案例(Spring3.x企业应用开发实战 p170)

MailSendEvent:

public class MailSendEvent extends ApplicationContextEvent{

    private String to;
    
    public MailSendEvent(ApplicationContext source,String to) {
        super(source);
        this.to = to;
    }

    public String getTo() {
        return to;
    }
}

MailSendListener:

public class MailSendListener implements ApplicationListener<MailSendEvent>{

    @Override
    public void onApplicationEvent(MailSendEvent event) {
        MailSendEvent mse = (MailSendEvent)event;
        System.out.println("MailSendListener:向"+mse.getTo()+"发送完一封邮件");
    }
}

MailSender:

public class MailSender implements ApplicationContextAware{

    private ApplicationContext ctx;
    
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.ctx = applicationContext;
    }
    
    public void sendMail(String to){
        System.out.println("MailSender:模拟发送邮件...");
        MailSendEvent mse = new MailSendEvent(this.ctx, to);
        //向容器中所有事件监听器发送事件
        ctx.publishEvent(mse);
    }
}

配置文件:

<bean class="event.MailSendListener"></bean>    
<bean id="mailSender" class="event.MailSender"></bean>

Main:

ApplicationContext ctx = new ClassPathXmlApplicationContext("event.xml");
MailSender mailSender = (MailSender) ctx.getBean("mailSender");
mailSender.sendMail("123@123.com");

 

多配置模块加载

通常会将整个系统的配置信息按照某种关注点进行分割。这样,在加载整个系统的bean定义时,就需要让容器同时读入划分到不同配置文件的信息。相对于BeanFactory来说, ApplicationContext大大简化了这种情况下的多配置文件的加载工作,只要以String[]形式传入这些配置文件所在的路径,即可构造并启动容器。 

String[] locations = new String[]{ "conf/dao-tier.springxml", "conf/view-tier.springxml", "conf/business-tier.springxml"};
ApplicationContext container = new FileSystemXmlApplicationContext(locations);
// 或者
ApplicationContext container = new ClassPathXmlApplicationContext(locations);

//当然也可以使用通配符
ApplicationContext container = new FileSystemXmlApplicationContext("conf/**/*.springxml");

 







 

 

posted @ 2017-04-30 13:34  WeiLanZz  阅读(306)  评论(0编辑  收藏  举报