spring 中bean的各种管理(来源:复制)

时间:2022-02-25 18:59:55

3.1 Bean基本管理

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

 

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

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

 

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

view sourceprint?
1 ApplicationContext context = new ClassPathXmlApplicationContext("beans*.xml");

 

 

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

view sourceprint?
1 <beans>
2     <import resource="dao-config.xml">
3   
4     <bean id="bean1" class="...">
5     <bean id="bean2" class="...">
6 </beans>

 

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

 

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

view sourceprint?
1 <beans>
2     <bean id="dataSource" class="...">
3       
4     <alias name="dataSource" alias="device:dataSource">
5     <alias name="dataSource" alias="user:dataSource">
6     ...
7 </beans>

 

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

view sourceprint?
1 <beans ...>
2     <bean id="dataSource" name="device:dataSource,user:dataSource" class="...">
3 </beans>

 

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

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

view sourceprint?
1 public interface IMusicBox {
2     public void play();
3 }

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

view sourceprint?
1 public class MusicBoxFactory {
2     public static IMusicBox createMusicBox() {
3         return new IMusicBox() {
4             public void play() {
5                 System.out.println("Playing piano music");
6             }
7         };
8     }
9 }

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

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

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

view sourceprint?
1 <bean id="musicBoxFactoryBean" class="spring.chapter3.MusicBoxFactory"/>
2 <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。看下面的例子。

view sourceprint?
1 <bean id="sysMessage" class="test.Message" scope="prototype">

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

view sourceprint?
01 pakage test;
02   
03 import java.util.Date;
04   
05 public class Message {
06     private String sysMessage;
07       
08     public Message() {
09         sysMessage = "Now date: " + new Date().toString();
10     }
11       
12     public String toString() {
13         return sysMessage;
14     }
15 }

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

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

view sourceprint?
01 package test;
02   
03 public class MessageManager {
04     public void display() {
05         Message message = createMessage();
06         System.out.println(message);
07     }
08       
09     protected abstract Message createMessage();
10 }

     下面是配置信息:

view sourceprint?
1 <bean id="messageManager" class="test.MessageManager">
2     <lookup-method name="createMessage" bean="sysMessage"/>
3 </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 后置处理器必须通过下面类似的代码显式地去注册:

view sourceprint?
1 Resource resource = new FileSystemResource("applicationContext.xml");
2   
3 ConfigurableBeanFactory factory = new XmlBeanFactory(resource);
4   
5 //实例化一个BeanPostProcesser接口实现类BeanPostProcessorImpl的实例
6 BeanPostProcessorImpl beanPostProcessor = new BeanPostPrcessorImpl();
7   
8 //对于BeanFactory,必须显式地注册BeanPostProcessor
9 factory.addBeanPostProcessor(beanPostProcessor);

 

   11. 解析文字消息

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

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

view sourceprint?
1 <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
2     <property name="basename" value="messages"/>
3 </bean>

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

view sourceprint?
1 ApplicationContext context =
2     new ClassPathXmlApplicationContext("applicationContext.xml");
3           
4 Object[] arguments = {"Jack", Calendar.getInstance().getTime()};
5           
6 System.out.println(context.getMessage("hello", arguments, Locale.US));
7 System.out.println(context.getMessage("hello", arguments, Locale.PRC));

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