【原创】《Spring 2.0 技术手册》学习笔记——第三章 Bean、消息、事件

3.1 Bean基本管理

   1. BeanFactory接口定义了Object getBean(String, Class)方法,通过指定Bean定义文件中设置的名称,取得相应的Bean实例,并转换至指定的类

 

   2. ApplicationContext可以读取多个Bean定义文件,通过数组实现,如:

ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"beans-config.xml", "beans-config2.xml"});

 

     也可以指定*字符,下面的例子课读取Classpath下所有以"beans”开头的XML配置文件。但要注意此方法只在实际的文件系统中有用,如果是在.jar文件中,以下的指定无效:

ApplicationContext context = new ClassPathXmlApplicationContext("beans*.xml");

 

 

   3. 当需要多个Bean定义文件时,可以使用<import>标签,如:

<beans>
	<import resource="dao-config.xml">

	<bean id="bean1" class="...">
	<bean id="bean2" class="...">
</beans>

 

     <import>标签必须放置在<bean>标签之前,定义文件必须放置在同一个目录或是Classpath之中,以相对路径指定Bean定义文件位置。

 

   4. 指定Bean的别名有两种方法,其一是使用<alias>标签,如:

<beans>
	<bean id="dataSource" class="...">
	
	<alias name="dataSource" alias="device:dataSource">
	<alias name="dataSource" alias="user:dataSource">
	...
</beans>

 

     另一种方法是使用<bean>标签的name属性直接指定,多个别名用逗号隔开如:

<beans ...>
	<bean id="dataSource" name="device:dataSource,user:dataSource" class="...">
</beans>

 

   5. 使用静态工厂方法和工厂bean实例化bean

       可以通过静态工厂方法实例化bean。假设有如下一个接口:

public interface IMusicBox {
	public void play();
}

       创建MusicBoxFactory类,取得IMusicBox实例由静态方法creatMusicBox()负责:

public class MusicBoxFactory {
	public static IMusicBox createMusicBox() {
		return new IMusicBox() {
			public void play() {
				System.out.println("Playing piano music");
			}
		};
	}
}

       Bean的配置如下,注意此时musicBox的class属性值不是它本身的类,必须是能获得该类的实例的工厂类(在这个例子里是MusicBoxFactory)。

<bean id="musicBox" class="spring.chapter3.MusicBoxFactory" factory-method="createMusicBox"/>

       此外,工厂方法也可以不是静态的,例如上例中的creatMusicBox()方法也可以是一个实例方法,此时若要实例bean必须先得到工厂类的bean,具体配置如下:

<bean id="musicBoxFactoryBean" class="spring.chapter3.MusicBoxFactory"/>
<bean id="musicBox" factory-bean="musicBoxFactoryBean" factory-method="createMusicBox"/>

       注意:factory-bean指定一个工厂类的实例,Spring会用factory-bean指定的实例调用factory-method指定的方法,从而得到一个实例bean。

 

   6. Bean的生命周期

     如果使用BeanFactory来生、管理Bean,会尽量支持一下的声明周期:

     •  Bean的建立

        由BeanFactory读取Bean定义文件,并生成各个Bean实例。

     •  属性注入

        执行相关的Bean属性依赖注入。

     •  BeanNameAware的setBeanName()

        如果Bean类有实现org.springframework.beans.factory.BeanNameAware接口,则执行它的setBeanName()方法。

     •  BeanFactoryAware的setBeanFactory()

        如果Bean类有实现org.springframework.beans.factory.BeanFactoryAware接口,则执行它的setBeanFactory()方法。

     •  BeanPostProcessors的postProcessBeforeInitialization()

        如果有任何的org.springframework.beans.factory.config.BeanPostProcessors实例与Bean实例相关联,则执行BeanPostProcessors实例的postProcessBeforeInitialization()方法。

     •  InitializingBean的afterPropertiesSet()

        如果Bean类有实现org.springframework.beans.factory.InitializingBean,则执行它的afterPropertiesSet()方法。

     •  Bean定义文件中定义init-method

        可以再Bean定义文件使用init-method属性设置方法名称。如果设置了该属性,当代码运行到这个阶段,就会执行init-method属性指定的方法。

     •  BeanPostProcessors的postProcessAfterInitialization()

        如果有任何的BeanPostProcessors实例与Bean实例关联,则执行BeanPostProcessors实例的postProcessAfterInitialization()方法。

     •  DisposableBean的destroy()

        在容器关闭时,如果Bean类有实现org.springframework.beans.factory.DisposableBean接口,则执行它的destroy()方法。

     •  Bean定义文件中定义destroy-method

        在容器关闭时,可以在Bean定义文件使用destroy-method属性设置方法名称。如果设置了该属性,当代码运行到这个阶段,就会执行destroy-method属性指定的方法。

 

        如果所有的Bean都有相同的初始化方法名称和销毁方法名称,例如都命名为init()和destroy(),则可以在<beans>上定义default-init-method和default-destroy-method属性,这样Spring会自动执行每个Bean的init()方法与destroy()方法。

 

   7. 如果使用的是BeanFactory,那么只有在使用getBean()方法真正取得Bean是,才会实例化Bean。如果使用的是ApplicationContext,则会预先根据Bean定义文件将所有的Bean实例化。在这种模式下,可以在<bean>上设置lazy-init属性为true,这样ApplicationContext就不会再启动时实例化该Bean。

 

 

   8. Bean的继承

      Bean定义文件中,一个Bean可以继承另外一个Bean的配置,只要在该Bean定义时设置parent属性为要继承的Bean的id即可。被继承的Bean可以设置abstract属性为true,这样这个Bean就不能被实例化,只能作为其他Bean的父Bean被继承,这一点类似Java中的abstract类。另外,abstract属性为true的Bean不必指定class属性。

 

   9. Lookup Method injection

      Lookup Method injection主要是用在Singleton的Object中使用非Singleton的Bean时,通过lookup-method的那个方法来取得非Singleton的Bean。看下面的例子。

<bean id="sysMessage" class="test.Message" scope="prototype">

     这里建立了一个普通的bean,sysMessage。相关的类代码如下:

pakage test;

import java.util.Date;

public class Message {
	private String sysMessage;
	
	public Message() {
		sysMessage = "Now date: " + new Date().toString();
	}
	
	public String toString() {
		return sysMessage;
	}
}

     Message对象的功能时是取得系统的日期。

     现在设计一个MessageManager类,当调用它的display()方法时,会新建立一个Message对象并加以显示,注意这是个abstract类,其中包含一个abstract方法createMessage():

package test;

public class MessageManager {
	public void display() {
		Message message = createMessage();
		System.out.println(message);
	}
	
	protected abstract Message createMessage();
}

     下面是配置信息:

<bean id="messageManager" class="test.MessageManager">
	<lookup-method name="createMessage" bean="sysMessage"/>
</bean>

     其中的lookup-method元素的name属性指定一个抽象方法,bean属性指定徐要传入的bean。每次调用name属性指定的方法时,会传入一个bean属性指定的bean。这样一来,虽然messageManager在容器建立初期便已经创建完毕,但是每次执行display方法的时候都能获得一个全新的message对象,从而获得新的系统日期信息。

     如果用传统的方法,在MessageManager类中增加一个Message类型成员属性,并通过spring注入,每次调用display()方法时获得的日期都是同样的值,即使message对象的scope被指定为prototype。因为messageManager对象时在容器建立时就创建好的,已经耦合了一个固定的message对象,每次调用display()方法是,不会得到一个新的message对象。

 

  10. BeanPostProcessor接口

       如果这个接口的某个实现类被注册到某个容器,那么该容器的每个受管Bean在调用初始化方法之前,都会获得该接口实现类的一个回调。容器调用接口定义的方法时会将该受管Bean的实例和名字通过参数传入方法,进过处理后通过方法的返回值返回给容器。

       注意,假如我们使用了多个的BeanPostProcessor的实现类,那么如何确定处理顺序呢?其实只要实现Ordered接口,设置order属性就可以很轻松的确定不同实现类的处理顺序了。

       另外,BeanFactory和ApplicationContext对待bean后置处理器稍有不同。ApplicationContext会自动检测在配置文件中实现了BeanPostProcessor接口的所有bean,并把它们注册为后置处理器,然后在容器创建bean的适当时候调用它。部署一个后置处理器同部署其他的bean并没有什么区别。而使用BeanFactory实现的时候,bean 后置处理器必须通过下面类似的代码显式地去注册:

Resource resource = new FileSystemResource("applicationContext.xml");

ConfigurableBeanFactory factory = new XmlBeanFactory(resource);

//实例化一个BeanPostProcesser接口实现类BeanPostProcessorImpl的实例
BeanPostProcessorImpl beanPostProcessor = new BeanPostPrcessorImpl();

//对于BeanFactory,必须显式地注册BeanPostProcessor
factory.addBeanPostProcessor(beanPostProcessor);

 

   11. 解析文字消息

        ApplicationContext继承了org.spriingframework.context.support.MessageSource几口,可以使用getMessage()方法取得文字消息的资源文件,从而实现国际化和本地化消息的目的。

       可以简单的通过MessageSource的一个实现org.springframework.context.support.ResourceBundleMessageSource来取得国际化消息。需要注意的是一定要在bean配置文件中配置一个该类的bean,并制定其basename属性,该属性值即为资源文件的basename。例如:

<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
	<property name="basename" value="messages"/>
</bean>

       上述配置说明资源文件的名字为messages开头,例如:messages_zh_CN.properties或者messages_en_US.properties。有了上述配置后,可以很方便的使用getMessage()方法取得国际化消息:

ApplicationContext context =
	new ClassPathXmlApplicationContext("applicationContext.xml");
		
Object[] arguments = {"Jack", Calendar.getInstance().getTime()};
		
System.out.println(context.getMessage("hello", arguments, Locale.US));
System.out.println(context.getMessage("hello", arguments, Locale.PRC));

       这里的getMessage()方法有三个参数,第一个参数指明资源文件(messages_*_*.properties)中的键值对的键名;第二个参数是一个object类型的数组,表示假如消息内容含有{0},{1}等运行时参数时(例如:hello=hello, {0}!),可以用此数组将相关参数传入;第三个参数是指定需要显示的Locale类型,若指定为Locale.PRC,则说明需要显示该消息的汉语版本。

posted @ 2011-01-28 14:13  弱水流觞  阅读(468)  评论(0编辑  收藏  举报