精通Spring+4.x++企业开发与实践之Spring容器高级主题

Spring容器高级主题

内部工作机制

Spring的AbstractApplicationContext是ApplicationContext的抽象实现类,该抽象类的refresh()方法定义了Spring容器在加载配置文件后的各项处理过程这些处理过程清晰的刻画了Spring容器启动时所执行的各项操作。

synchronized (this.startupShutdownMonitor) {
		// Prepare this context for refreshing.
		prepareRefresh();
		//1.初始化BeanFactory:根据配置文件实例化BeanFactory,在obtainFreshBeanFactory()方法中,
		//首先调用refreshBeanFactory()刷新BeanFactory,然后调用getBeanFactory()获取BeanFactory,
		//这两个方法都是由具体子类实现的。在这一步,Spring将配置i文件信息装入容器的Bean定义注册表(
		//BeanDefinitionRegistry)中,但此时Bean还未初始化。
		// Tell the subclass to refresh the internal bean factory.
		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

		// Prepare the bean factory for use in this context.
		prepareBeanFactory(beanFactory);

		try {
			// Allows post-processing of the bean factory in context subclasses.
			postProcessBeanFactory(beanFactory);
			//2.调用工厂后处理器:根据反射机制从BeanDefinitionRegistry中找出所有实现了
			//BeanFactoryPostProcessor接口Bean,并且调用其postProcessBeanFactory()接口方法。
			// Invoke factory processors registered as beans in the context.
			invokeBeanFactoryPostProcessors(beanFactory);
			//3.注册Bean后处理器:根据反射机制从BeanRegistry中找出所有实现了BeanPostProcessor接口的
			//的Bean,并且将它们注册到容器Bean后处理器的注册表中。
			// Register bean processors that intercept bean creation.
			registerBeanPostProcessors(beanFactory);
			//4.初始化消息源:初始化容器的国际化消息资源。
			// Initialize message source for this context.
			initMessageSource();
			//5.初始化应用上下文事件广播器
			// Initialize event multicaster for this context.
			initApplicationEventMulticaster();
			//6.初始化其他特殊的Bean:这是一个钩子方法,子类可以借助这个方法执行一些特殊的操作,如
			//AbstractRefreshableWebApplicationContext就使用该方法执行初始化ThemeSource的操作
			// Initialize other special beans in specific context subclasses.
			onRefresh();
			//7.注册事件监听
			// Check for listener beans and register them.
			registerListeners();
			//8.初始化所有单实例的Bean,使用懒加载模式的Bean除外:初始化Bean之后将它们放入Spring容器的
			缓冲池。
			// Instantiate all remaining (non-lazy-init) singletons.
			finishBeanFactoryInitialization(beanFactory);
			//9.完成刷新并发布容器刷新事件:创建上下文刷新时间,时间广播器负责将这些时间广播到每个注册的
			//时间监听器中
			// Last step: publish corresponding event.
			finishRefresh();
		}

		catch (BeansException ex) {
			if (logger.isWarnEnabled()) {
				logger.warn("Exception encountered during context initialization - " +
						"cancelling refresh attempt: " + ex);
			}

			// Destroy already created singletons to avoid dangling resources.
			destroyBeans();

			// Reset 'active' flag.
			cancelRefresh(ex);

			// Propagate exception to caller.
			throw ex;
		}

		finally {
			// Reset common introspection caches in Spring's core, since we
			// might not ever need metadata for singleton beans anymore...
			resetCommonCaches();
		}
	}

IoC的流水线图:

1.ResourceLoader从存储介质加载Spring配置信息,并使用Resource表示这个配置文件资源。

2.BeanDefinitionReader读取Resource所指向的配置文件资源,然后解析配置我呢见。配置文件中的每个<bean>解析成一个BeanDefinition对象,并保存到BeanDefinitionRegistry中。

3.容器扫描BeanDefinitionRegistry中的BeanDefinition,使用Java反射机制自动识别出Bean工厂的后处理器(实现BeanFactoryPostProcessor接口的Bean),然后调用这些Bean工厂后处理器对BeanDefinitionRegistry中的BeanDefinition进行加工处理。主要完成以下两项工作。
		1. 对使用占位符的<bean>元素进行解析,得到最终的配置值。这意味着对一些半成品式的BeanDefinition对象进行架构处理并得到成品的BeanDefinition对象。
		2.对BeanDefinitionRegistry中的BeanDefinition进行扫描,通过Java反射机制找出所有属性编辑器的Bean(实现了java.beans.PropertyEditor接口的Bean),并自动将它们注册到Spring容器的属性编辑器注册表中(org.springframework.beans.PropertyEditorRegistry).
4.Spring容器从BeanDefinitionRegistry中取出后加工后的BeanDefinition,并调用InstantiationStrategy着手进行Bean实例化的工作。
5.实例化Bean时,Spring容器使用BeanWrapper对Bean进行封装,BeanWrapper提供了很多以Java反射机制操作Bean的方法,它将结合Bean的Definition及容器中的属性编辑器,完成属性注入工作。
6.利用容器注册的Bean进行后处理器(实现BeanPostProcessor接口的Bean)对已经完成属性设置工作的Bean进行后续的加工,直接装配出一个准备就绪的Bean.

Spring组建按其所承担的角色可划分未两类。

物料组件:Resource,BeanDefinition,PropertyEditor及最终的Bean等,它们时加工流程被加工,被消费的组建,就像流水线上被加工的物料一样。
设备组件:ResourceLoader,BeanDefinitonReader,BeanFactoryPostProcessor,InstantiationStrategy及BeanWrapper等。它们就像流水线上不同环节的设备加工设置,对物料组建进行加工处理。

BeanDefinition

org.springframework.beans.factory.config.BeanDefinition是配置文件<bean></bean>元素标签在容器中的内部表示。<bean></bean>元素标签用于class,scope,lazy-init等配置属性,BeanDefinition则提供了相应的beanClass,scope,lazyInit类属性,BeanDefinition就像<bean></bean>的镜像。

BeanDefinition类的集成结构如下:

RootBeanDefinition是最常用的实现类,它对应一般性的<bean></bean>元素标签。配置文件可以定义父子bean,父bean使用RootBeanDefinition,子Bean使用ChildBeanDefinition表示,而没有父bean就默认使用RootBeanDefinition表示。 AbstractBeanDefinition对二者共同的类信息进行抽象。

BeanDefinition是对配置文件中的<bean></bean>进行表示,最终将BeanDefinition会被存储在Spring的内存数据库BeanDefinitionRegistry中,后续的操作直接从这个数据库中直接获取,BeanDefinition只在容器启动时加载并解析,除非容器刷新或者重启,这些信息不会发生变化。

InstantiationStrategy

org.springframework.beans.factory.support.InstantiationStrategy负责根据BeanDefinition对象创建一个Besan实例。Spring之所以将实例化Ban共工作通过一个策略接口进行描述,是为了可以方便的采用不同的实例化策略,以满足不太的应用需求,如通过CGLib类库为Bean动态创建子类再进行实例化,InstantiationStrategy类的继承结构如下: 1.SimpleInstantiationStrategy是最常用的实例化策略,该策略理由Bean实现类的默认构造函数,带参构造函数或工厂方法创建Bean的实例。

2.CglibSubclassingInstantiationStrategy拓展了SimpleInstantiationStrategy,为需要进行方法注入的Bean提供了支持,然后使用这个动态生成的子类创建bean的实例。

InstantiationStrategy仅负责实例化Bean的操作,相当于执行Java语言的new操作,它只负责实例化Bean,没有涉及任何的属性设置,实际上得到的是一个半成品的Bean,属性操作交给BeanWrapper

BeanWrapper

属性的设置由BeanWrapper这个代理器完成,InstantiationStratedy创建出Bean实例之后,BeanWrapper来完成属性填充的工作,容器主控程序将Bean实例通过BeanWrapper包装起来,这是通过调用BeanWrapper#setWrappedInstance(Object obj)方阿飞完成的。BeanWrapper类的继承结构如下:

PropertyAccessor和PropertyEditorRegistry是BeanWrapper的两个顶级接口,PropertyAccessor定义了各种访问Bean属性的接口,如setPropertyValue(String,Object),setPropertyValues(PropertyValue pvs)等;而PropertyEditorRegistry是属性编辑器的注册表.BeanWrapper实现类BeanWrapperImpl作用: 1.Bean包裹器 2.属性访问器 3.属性编辑器注册表

一个BeanWrapperImpl实例内部封装了两类组件:1.被封装的待处理的Bean,以及一套用于设置Bean属性的属性编辑器。 属性的注入需要从BeanDefinitionRegistry中获取到BeanDefinition,然后Spring的主控程序从BeanDefinition中获取Bean的属性的配置信息PropertyValue,并且使用属性编辑器对PropertyValue进行装换以得到Bean的属性值。BeanWrapper内部使用了Spring的BeanUtils工具类对Bean进行反射曹祖哦,设置属性。

属性编辑器

Sun所制定的JavaBean规范很大程度上是为IDE准备的——它让IDE能够以可视化的方式设置avaBean的属性,如果在IDE中开发一个可视化的应用程序,则需要通过属性设置的方式对组成的各种组件进行定制,IDE通过属性属性编辑器让开发人员使用可视化的方式设置组件的属性。

IDE都支持JavaBean规范所定义的属性编辑器,当组件开发商发布一个组件时,它往往将组件对应的属性编辑器捆绑发行,这样开发者就可以在IDE环境下方便地利用属性编辑器对组件进行定制工作。

JavaBean规范通过java.beans.PropertyEditor定义了设置JavaBean属性地方法,通过BeanInfo描述了JavaBean描述了JavaBean的哪些属性是可以定制的,此外描述了可定制属性与PropertyEditor的对应关系。

BeanInfo与JavaBean之间的的对应关系通过二者之间的命名的规范确立。对应JavaBean的BeanInfo采用如下的命名规范:<Bean>BeanInfo.例如ChartBean对应的BeanInfo为ChartBeanInfo.当JavaBean连同其属性编辑器以通注册到IDE中后,在开发界面中对JavaBean进行定制时,IDE就会根据JavaBean规范找到对应的BeanInfo,再根据BeanInfo的描述信息找到BeanInfo中描述西悉尼找到JavaBean属性描述(是否开发,使用那个属性编辑器),进而为JavaBean生成特定的开发编辑界面.

JavaBean规范提供了一个管理默认属性编辑器的管理器PropertyEditorManager,该管理器内保存者一些常见的类型的属性编辑器。如果某个JavaBean的常见属性没有通过BeanInfo显式地指定属性编辑器,那么IDE将自动使用PropertyEditorManager中注册地对应默认地属性编辑器。

PropertyEditor

PropertyEditor是属性编辑器地接口,规范了将外部地设置值转换为内部的JavaBean属性值的转换接口方法,PropertyEditor主要的接口方法: 1.getValue():返回属性的当前值,基本类型被封装成对应的封装类实例。 2.setValue(Object value):设置属性值,基本类型以疯传类传入

3.String getAsText():将属性对象用字符串表示,以便外部的属性编辑器能以可视化的方式显示。默认的返回null,表示该属性不能以字符串表示。
4.void setAsText(String text):用一个属性去更新属性的内部值,这个字符串一般从外部属性扁你机器传入。
5.String[] getTags():返回不鸟事有效属性值的字符串数组(如boolean对应的由效Tag为true和false),以便属性编辑器能以下拉框方式显示出来。默认返回null,表示属性没有匹配的字符候选值有些集合。
6.String getJavaInitializationString():为属性提供一个表示初始值得字符串,属性编辑器以此值作为属性得默认值。

PropertyEditor接口方法是内部属性值和外部属性值得沟通桥梁,是专门为IDE中得可视化属性编辑器提供得。

BeanInfo

BeanInfo主要描述了JavaBean的哪些属性可以编辑对应的属性编辑器,每个属性对应一个属性描述器PropertyDescriptor。构造函数public PropertyDescriptor(String propertyName, Class<?> beanClass),其中propertyName:属性名,beanClass:javaBean对应的class。此外PropertyDescriptor还有一个方法 public void setPropertyEditorClass(Class<?> propertyEditorClass)方法,用于为JavaBean属性指定编辑器。BeanInfo接口最重要的方法就是PropertyDescriptor[] getPropertyDescriptors(),该方法返回JavaBean的属性描述器数组。 BeanInfo接口的一个常用实现类是SimpleBeanInfo,一般情况下,可以通过拓张SimpleBeanInfo实现自己的功能。

Spring默认的属性编辑器

Spring的属性编辑器和传统用于IDE开发的属性编辑器不同,它没有UI界面,仅负责将配置文件中的文件配置值转换为Bean属性的对应值,所以Spring的属性编辑器并非传统意义上的JavaBean属性编辑器。 Spring为常见的属性累提供了默认的属性编辑器。BeanWrapperImpl类拓展了PropertyEditorRegistrySupport类,Spring在PropertyEditorRegistrySupport中为常见属性类型提供了默认的属性编辑器。

PropertyEditorRegistrySupport有两个变量 Map<Class<?>, PropertyEditor> defaultEditors:用于保存默认属性类型的编辑器,元素的键为类的属性类型,值为对应的属性编辑器实例。 Map<Class<?>, PropertyEditor> customEditors:用于保存用户自定义的属性编辑器,元素的键值和defaultEditors相同。 ![](htt ps://oscimg.oschina.net/oscnet/fead06d498367e6b6b83997e312b3df412b.jpg)

这些默认的属性编辑器用于解决常见的属性类型的是注册问题。如果用户的应用包括一些特殊的类型的属性的话,而且希望配置文件中以字面值提供配置,那么就需要编写自定义属性编辑器注册到Spring容器中。这样,Spring才能将配置文件中的配置属性配置值转换为对应的属性类型值。

自定义属性编辑器

Spring的带份默认属性编辑机器都直接拓展于java.beans.PropertyEditorSupport类,开发者也可以通过拓展PropertyEditorSupport实现自己的属性编辑器。相对于用于IDE环境的属性百年机器来说,Spring环境下使用的属性编辑器的功能比较单一,仅需要将配置文件中的字面值转换为属性类型的对象即可,并不需要提供UI界面,因此需要简单覆盖PropertyEditorSupport的setAsText()即可。

例子: 1.Boss.java

public class Boss {
private String name;
private Car car = new Car();

public Car getCar() {
	return car;
}

public void setCar(Car car) {
	this.car = car;
}

public String getName() {
	return name;
}

public void setName(String name) {
	this.name = name;
}

[@Override](https://my.oschina.net/u/1162528)
public String toString() {
	return "Boss{" +
			"name='" + name + '\'' +
			", car=" + car +
			'}';
}
}

Car.java

public class Car {
private int maxSpeed;
public String brand;
private double price;
public String getBrand() {
	return brand;
}

public void setBrand(String brand) {
	this.brand = brand;
}

public int getMaxSpeed() {
	return maxSpeed;
}

public void setMaxSpeed(int maxSpeed) {
	this.maxSpeed = maxSpeed;
}

public double getPrice() {
	return price;
}

public void setPrice(double price) {
	this.price = price;
}

public String toString(){
	return "brand:"+brand+"/maxSpeed:"+maxSpeed+"/price:"+price;
}
}

自定义属性编辑器CustomerCarEditor.java

public class CustomerCarEditor extends PropertyEditorSupport {

	//将字面值转换为属性类型对象
	[@Override](https://my.oschina.net/u/1162528)
	public void setAsText(String text) throws IllegalArgumentException {
		if (StringUtils.isEmpty(text)||text.indexOf(",")==-1){
			throw new IllegalArgumentException("设置的字符串格式不正确");
		}
		String[] infos = text.split(",");
		Car car = new Car();
		car.setBrand(infos[0]);
		car.setMaxSpeed(Integer.valueOf(infos[1]));
		car.setPrice(Double.valueOf(infos[2]));
		//调用父类得setValue()方法设置转换后得属性对象
		setValue(car);
	}
}

beans.xml配置文件:

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans
		 http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">

	<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
		<property name="customEditors">
			<map>
				<entry key="com.flexible.editor.editorbean.Car"
					   value="com.flexible.editor.editorbean.CustomerCarEditor"/>
			</map>
		</property>
	</bean>

	<bean id="boss" class="com.flexible.editor.editorbean.Boss">
		<property name="name" value="John"/>
		<property name="car" value="红旗CA72,200,20000.00"/>
	</bean>

</beans>

测试代码:

    ApplicationContext context = new ClassPathXmlApplicationContext("classpath:editor/beans");
    Boss boss = (Boss) context.getBean("boss");
    System.out.println(boss.toString());

执行结果:

Boss{name='John', car=brand:红旗CA72/maxSpeed:200/price:20000.0}

PropertyPlaceholderConfigurer属性文件

PropertyPlaceholderConfigurer能够使Bean在配置时引用外部属性文件。PropertyPlaceholderConfigurer实现了BeanFactoryPostProcessorBean接口。

PropertyPlaceholderConfigurer属性文件

jdbc.properties

dbName=sampledb
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/${dbName}
#userName=root
#password=123456
userName=WnplV/ietfQ=
password=QAHlVoUc49w=

beans.xml

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
	   xmlns:context="http://www.springframework.org/schema/context"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans
		 http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
		 http://www.springframework.org/schema/context
		 http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<!--引入jdbc.properties属性文件-->
<bean class="org.springframework.beans.factory.config.PreferencesPlaceholderConfigurer"
p:location="classpath:placeholder/jdbc.properties"
	  p:fileEncoding="utf-8">
</bean>
<!---->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close"
p:driverClassName="${driverClassName}"
p:url="${url}"
p:username="${userName}"
p:password="${password}"
></bean>
</beans>

propertyPlaceholderConfigurer的其他属性 1.locations:如果只有一个属性文件,直接使用location指定就可以,如果有多个属性文件,则可以通过locations属性进行设置。可以像配置List一样配置locations属性。

2.fileEncoding:属性文件的编码格式。Spring使用了操作系统默认的编码戈斯和,如果采用了特殊的编码格式需要通过该属性显式指定。

3.order:如果配置i文化你中定义了多个PropertyPlaceholderConfigurer,则通过该属性指定有限顺序

4.placeholderPrefix:在上面的例子中,可可以通过${属性名}引用属性文件中的属性项,其中"${为占位符前缀","}"是暂未付后缀

可以使用<context:property-placeholder location="classpath:placeholder/jdbc.properties"></context:property-placeholder>引入属性文件,但是如果需要一些高级功能(加密)

基于注解及基于Java类的配置中引用属性

国际化信息

为每种语言提供一套相应的资源文件俺,并以规范化的命名的方式保存在特定的目录中,由系统自动根据客户端语言选择合适的资源文件。"国际化信息"也成为"本地化信息",一般需要两个条件才可以确定,Java通过java.util.Local类白哦是一个本地化对象,它允许通过语言参数和国家/地区参数创建以一个确定的本地化对象。 例如: public class LocalUtils {

public static Locale myLocal(){
    //带有语言和国家/地区的本地化对象
    Locale local = new Locale("zh","CN");
    //只有语言信息的本地化对象。
    Locale locale2 = new Locale("zh");
    //等同于Locale("zh","CN")
    Locale locale3 = Locale.CHINA;
    //等同于Locale("zh")
    Locale locale4 = Locale.CHINESE;
    //获取本地系统默认的本地化对象
    Locale locale5 = Locale.getDefault();
    return locale5;
}
}

注意: 在测试的时候如果需要该百年系统默认的本地化设置,则可以在启动JVM时通过命令参实指定:java -Duser.language=en -Duser.region=US MyTest

本地化工具

NumberFormart,DateFormart,MessageFormat。MessageFormat在NumberFormart和DateFormat的基础之上提供了占位符字符串的格式换的功能,支持时间,货币,数组以及对象属性格式化操作。

例如: Locale locale = new Locale("zh","CN"); NumberFormat format = NumberFormat.getCurrencyInstance(locale); double num = 123456.78; System.out.println(format.format(num));

    //格式化信息串
    String pattern1 = "{0},你好!你于{1}我行存入了{2}元";
    //用于动态替换占位符的参数
    Object[] params = {"张三",new GregorianCalendar().getTime(),1000000.0E3};
    //使用默认的本地化对象格式化信息
    String message1 = MessageFormat.format(pattern1,params);
    System.out.println(message1);

    String pattern2 = "At {1,time,short} On {1,date,long},{0} paid {2,number,currency}.";
    MessageFormat messageFormat = new MessageFormat(pattern2,Locale.US);
    String result = messageFormat.format(params);
    System.out.println(result);

执行结果: CN ¥123,456.78 张三,你好!你于18-11-19 下午12:51我行存入了1,000,000,000元 At 12:51 PM On November 19, 2018,张三 paid $1,000,000,000.00.

ResourceBundle 如果需要国际化的支持,就必须为不同的本地化类型非被提供对应的资源文件,并以锅饭的方式进行命名: <资源名><语言代码><国家/地区代码>.properties,其语言代码和读取代码都是可选的。<资源名>.properties命名的国际化资源文件是默认的资源文件,即某个本地换化类型在系统找不到的对应的资源文件,就才有这个默认的资源文件。 例如:

    Locale locale = new Locale("zh","CN");
    Locale locale2 = new Locale("en","US");
    ResourceBundle resourceBundle = ResourceBundle.getBundle("com/flexible/message",locale);
    ResourceBundle resourceBundle2 = ResourceBundle.getBundle("com/flexible/message",locale2);
    System.out.println(resourceBundle.getString("userName"));
    System.out.println(resourceBundle2.getString("userName"));

执行结果: 张三 zhangsan

MessageSource

Spring定义了访问国际化信息的MessageSource接口,并且提供了若干个易用的实现类,这些重要的方法如下:

1.String getMessage(String code, Object[] args, String defaultMessage, Locale locale):code表示国际化信息中的属性名称,args用于传递格式化串占位符所用的运行期参数;当在资源中找不到对应的属性名称时,返回defaultMessage参数指定的默认信息,locale表示本地化对象。

2.String getMessage(String code, Object[] args, Locale locale)throws NoSuchMessageException:与1的方法一样,只是在找不到资源的时候抛出NoSuchMessageException

3.String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException:将属性名,参数数组以及默认信息封装起来,他的功能和第一个接口方法相同。

MessageSource的类结构

HierarchicalMessageSource:接口添加了两个方法,建立了父子层级的MessageSource结构,类似于前面介绍的HierachicalBeanFactory.该接口的setParentMessageSource(MessageSource parent)放啊用于设置父MessageSource和MessageSource getParentMessageSource()返回父MessageSource

ResourceBundleMessageSource和ReloadableResourceBundleMessageSource是HierarchicalMessageSource两个重要的子类。

ResourceBundleMessageSource

允许用户通过beanName制定一个字眼名(包括类路径的全限定资源名),或者通过beanNames指定一组资源名。使用ResourceBundleMessageSource例子如下:

		String[] configs = {"message/beans"};
		ApplicationContext context = new ClassPathXmlApplicationContext(configs);
		//获取MessageSource的Bean
		MessageSource ms = (MessageSource)context.getBean("mySource");
		Object[] params = {"jack",new GregorianCalendar().getTime()};
		//获取格式化的国际信息
		String str1 = ms.getMessage("userName",params, Locale.US);
		System.out.println(str1);

xml配置文件:

<bean id="mySource" class="org.springframework.context.support.ResourceBundleMessageSource">
	<property name="basenames">
		<list>
			<value>com/flexible/message</value>
		</list>
	</property>
</bean>

执行结果: zhangsan

ReloadableResourceBundleMessageSource

该类可以是实现资源的定时刷新

<bean id="mySource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
	<property name="basenames">
		<list>
			<value>com/flexible/message</value>
		</list>
	</property>
	<property name="cacheSeconds" value="5"></property>
</bean>

容器级的国际化信息资源

Spring正常情况下时不会将国际化资源作为Bean注入其他的Bean当中,而是做为容器的基础设施向所有的bean开放。在前面已经介绍过initMessageSource()就是初始化容器中的国际化信息资源,它根据反射机制从BeanDefinitionRegistry中找出名为messageSource且类型为org.springframework.context.MessageSource的Bean,将这个Bean定义的信息资源加载为容器级别的国际化信息资源。 例如:

		ApplicationContext context = new ClassPathXmlApplicationContext("containermessagesouece/beans");
		String userName1 = context.getMessage("userName", params, Locale.CHINA);
		String userName2 = context.getMessage("userName", params, Locale.US);
		System.out.println("userName1:"+userName1+"---"+"userName2:"+userName2);

beans.xml

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
	   xmlns:context="http://www.springframework.org/schema/context"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans
		 http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
		 http://www.springframework.org/schema/context
		 http://www.springframework.org/schema/context/spring-context-4.0.xsd">


//这个地方只能命名为messageSource,否则就会抛出NoSuchMessageException
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
	<property name="basenames">
		<list>
			<value>com/flexible/message</value>
		</list>
	</property>
</bean>
</beans>

容器事件

猜你喜欢

转载自my.oschina.net/u/3474937/blog/2875890