spring学习笔记 第三章

spring学习笔记 第三章
 

3.1 Bean基本原理

BeanFactory 负责读取Bean的定义文件:管理对象的加载,生成,维护,与对象之间的依赖关系。提供的功能比较简单。

ApplicationContext 提供一些特色以及高级的容器功能。大概有3个实现类:

l         FileSystemXmlApplicationContext

l         ClassPathXmlApplicationContext

l         XmlWebApplicationContext

当需要多个Bean定义文件,Spring建议使用ApplicationContext的方法读取,好处

Bean定义文件之间嗜独立的。

u       一个替代的方式是使用<import>标签,如:

       <beans …>

              <import resource=”dao-config.xml”/>

              <import resource=”resources/messageSource.xml”/>

              ……

       </beans>

u       另外的方法如下:

ApplicationContext context = new ClassPathXmlApplicationContext(

       new String[]{“beans-config.xml”, ”beans2-config.xml”});

使用file:/,classpath:甚至http://url致,或者classpath*: 表示

classpath前致路径都匹配。

ApplicationContext context =

new ClassPathXmlApplicationContext(“classpath*:beans-config.xml”);

       制定*字符,以下的例子可读取ClassPath下所有以”beans”开头的xml配置文件,但耀注意的是此方法只在实际的文件系统中有用,如果是.jar文件,以下的指定无效:

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

 

XmlWebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(this.getServletConfig().getServletContext());

l        Bean的实例化

1)        最简单的最基本的,也就是没有参数的构造方法。

<bean id=”writer” class=”com.test.FloppyWriter”/>

2)        有构造方法的,代码如下:

public class HelloBean {

    private String name;

    private String helloWorld;

    public HelloBean(){}

    public HelloBean(String name, String helloWorld) {

       this.name = name;

       this.helloWorld = helloWorld;

    }

    public String getName() { return name; }

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

    public String getHelloWorld() { return helloWorld; }

    public void setHelloWorld(String hel) { this.helloWorld = hel; }

    public static void main(String args[]) {

       AbstractApplicationContext context =

           new ClassPathXmlApplicationContext("beans.xml");

       HelloBean hello = (HelloBean)context.getBean("hellBean");

       System.out.println(hello.getHelloWorld()+","+hello.getName());

    }

}

    <bean id="hellBean" class="com.spring.ch3.HelloBean">

       <constructor-arg index="0">

           <value>大家好</value>

       </constructor-arg>

       <constructor-arg index="1">

           <value>Hello</value>

       </constructor-arg>

    </bean>

3)      通过静态构造方法来取得某个对象,好处是调用静态工厂方法的对象不用了解对象建立的细节,例子如下:

interface IMusicBox {public void play();}

class MusicBoxFactory {

    public static IMusicBox createMusicBox() {

       return new IMusicBox() {

           public void play() {

              System.out.println("弹奏吉他的声音...");

           }

       };

    }

    public static void main(String args[]) {

       AbstractApplicationContext context =

           new ClassPathXmlApplicationContext("beans.xml");

       IMusicBox box = (IMusicBox)context.getBean("musicbox");

       Box.play();

    }

}

<bean id="musicbox" class="com.spring.ch3.MusicBoxFactory"

       factory-method="createMusicBox" />

或者

问题

<bean id="factoryBean" class="com.spring.ch3.MusicBoxFactory" />

<bean id="musicbox" factory-bean="factoryBean" factory-method="createMusicBox" />

为什么不行

 

l        Beanscope

    Spring中取得的实例默认为Singleton,也就是默认每一个Bean名称只维持一个实例。也就是说getBean(“xxx”)的时候实际上都是同一个对象。使用Singleton模式产生的单一实例,对单线程的程序来说不会有什么问题,单对于多线程的程序,必须注意Thread-safe的问题,防止多个线程同时存取公用资源所引发的问题,通常SingletonBean都是无状态的(Stateless)。可以通过设置,每次从BeanFactory或者ApplicationContext中制定别名取得Bean都产生一个新的实例,如:

<bean id=”helloBean” class=”XXXXX” scope=”prototype”>

...

</bean>

    Spring中,”scope”属性预设为”singleton”,通过将其设置为”prototype”,使得每次制定名称来取得Bean,都会产生一个新的实例; 也可以设置<bean>”singleton”属性为”true””false”,来设置是否Singleton的方式来产生实例,不过这个主要是为了和前面的版本兼容而保留的。在Spring中,”Scope”除了可以设置为”singleton””prototype”,

针对Web应用程序,还可以设置”request”,”session”,”GlobalSession”,分别表示请求阶段,会话阶段,和基于ProtletWeb应用程序会话阶段.

 

l        Bean的生命周期

一个Bean从建立到销毁,会经历几个执行阶段,如果使用BeanFactoyr来生成,管理Bean,会尽量支持以下的生命周期。

1)      Bean的建立   

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

2)      属性注入      

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

3)      BeanNameAwaresetBeanName()      

如果Bean类有实现BeanNameAware接口,则执行它的setBeanName()

4)      BeanFactoryAwaresetBeanFactory()   

如果Bean类有实现BeanFactoryAware接口,则执行它的setBeanFactory()

5)      BeanPostProcessorsprocessBeforeInitialization()

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

6)      InitializingBeanafterPropertiesSet()

如果Bean有实现InitializingBean,则执行他的afterPropertiesSet()方法。

7)      Bean定义文件中的init-method

可以在Bean定义文件中使用”init-method”属性设置方法名称,如:

...

<bean id=”helloBean” class=”XXXXX” init-method=”initBean”>

...

8)      BeanPostProcessorsprocessaAfterInitialization()

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

9)      DisposableBeandestroy()

在容器关闭的时候,如果Bean类有实现DisposableBean接口,则执行他的destroy()方法。

10)  Bean定义文件中定义的destroy-method

在容器关闭时,可以在Bean定义文件使用”destroy-method”属性设置方法名称,如:

...

<bean id=”helloBean” class=”XXXXX” destroy-method=”destroyBean”>

...

如果有以上的设置,当代码运行到这个阶段的时候,就会执行的destroyBean方法。

定义<bean>”init-method”属性,其实与实现InitializingBeanafterPropertiesSet()意义是相同的。采用前者,可以定义Bean的初始化方法而不用耦合至SpringAPI。定义<bean>”destroy-method”属性,与实现DisposableBean接口的destroy()方法的意义也是相同的。

11)  另外

可以在<beans>上定义”default-init-method””default-destroy-method”属性,Spring会自动执行每个Bean上所定义的init()destroy()方法,例如:

<beans default-init-method=”init” default-destroy-method=”destroy”>

...

</beans>

    如果使用BeanFactory,只用在使用getBean()取得Bean时,才会做实例化的动作。如果使用ApplicationContext,则会预先针对Bean定义文件的内容,将所有的Bean实例化。在<bean>上设置”lazy-int”属性为”true”ApplicationContext 就不会在启动针对该Bean做实例化动作,例如:

<bean id=”helloBean” class=”XXXXX” lazy-init=”true”>

    ...

</bean>

 

l        Bean的生命周期

如果Bean定义文件的内容不断的增加,而你发现有些Bean的定义其实有所重复。如,有好几个Bean定义都有”name””age”等属性,而大部分设置都是相同的值,只有几个Bean会有不同的设置,则可以考虑继承某个Bean定义,这样可以省去许多设置的功夫。

class SomeBean {

    private String name;

    private int age;

    public String getName() { return name; }

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

    public int getAge() { return age; }

    public void setAge(int age) { this.age = age; }

    public static void main(String args[]) {

        AbstractApplicationContext context =

            new ClassPathXmlApplicationContext("beans.xml");

        SomeBean bean = (SomeBean)context.getBean("some");

        System.out.println("SomeBean->"+bean.getName());

        System.out.println("SomeBean->"+bean.getAge());

    }

}

<bean id="inheritedSomeBean" abstract="true">

        <property name="name"><value>Guest</value></property>

        <property name="age"><value>18</value></property>

    </bean>

    <bean id="some" class="com.spring.ch2.SomeBean" parent="inheritedSomeBean">

        <property name="name"><value>测试人员</value></property>

    </bean>

运行结果为:

SomeBean->测试人员

SomeBean->18

   

 

3.2 Bean的依赖设置

    当构造方法上的参数个数相同时,Spring会自动解析构造方法上的参数类型及所设置的依赖注入,用来决定要使用哪个构造方法,例如:

public class HelloBean {

    private String name;

    private Date date;

    public HelloBean(Date date) { this.date = date; }

    public HelloBean(String name) { this.name = name; }

    public HelloBean(String name, Date date) {

       this.name = name;

        this.date = date;

    }

    public String getName() { return name; }

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

    public Date getDate() { return date; }

    public void setDate(Date date) { this.date = date; }

public static void main(String args[]) {

        AbstractApplicationContext context =

           new ClassPathXmlApplicationContext("beans.xml");

       HelloBean bean = (HelloBean)context.getBean("helloBean");

       System.out.println("SomeBean->"+bean.getName());

       System.out.println("SomeBean->"+bean.getDate().toString());

    }

}

<bean id="date" class="java.util.Date"/>

<bean id="helloBean" class="com.spring.ch3.HelloBean">

    <constructor-arg><ref bean="date"/></constructor-arg>

</bean>

运行结果为:

SomeBean->null

SomeBean->Thu Aug 21 11:33:12 CST 2008

 

可以设置”type”属性来明确制定要使用哪个类型,如:

<bean id="date" class="java.util.Date"/>

<bean id="helloBean" class="com.spring.ch3.HelloBean">

    <constructor-arg type=”java.util.Date”><ref bean="date"/></constructor-arg>

</bean>

 

l        依赖的值设置与参考

1)      设置例子1

<bean id="helloBean2" class="com.spring.ch3.HelloBean">

    <constructor-arg><value>大家好</value></constructor-arg>

    <property name="date"><null/></property>

    </bean>

    注意: <property name="date"><value></value></property>是把属性设置为空字符串,而不是设这为null

2)      设置例子2

<bean id="helloBean2" class="com.spring.ch3.HelloBean">

    <constructor-arg value=”大家好></constructor-arg>

    <property name="name" value=”XXX”></property>

    </bean>

3)      如果Bean定义文件中已经有一个定义的Bean实例,则可以直接让某个属性参考这个实例,例如:

<bean id=”helloBean” class=”XXXXX”>

    <constructor-arg><ref bean=”date”/></constructor-arg>

    <property name=”other”><ref bean=”otherBean”/></property>

</bean>

另一个比较简介的写法,使用”ref”属性来制定,例如:

<bean id=”helloBean” class=”XXXXX”/>

<constructor-arg ref=”date”/>

<property name=”other” ref=”otherBean”/>

</bean>

4)      如果希望使用<ref>参考其他Bean实例时,所定义的Bean必须是同一个设置文件中,且可以指定”local”属性,例如:

<bean id=”helloBean”>

    <property name=”other”><ref local=”otherBean”/></property>

</bean>

5)      如果某个Bean实例只被某个属性参考过一次,之后在定义文件中再也不被参考,则可以直接在属性定义的时候使用<Bean>标签,并且需要指定其”class”属性即可,例如:

<bean id=”helloBean” class=”XXXXX”>

    <property name=”helloWorld” value=”hello”></property>

    <property name=”date”><bean class=”java.util.Date”/></property>

</bean>

6)      在取得某个Bean之前,如果它依赖于另一个BeanSpring就会先去实例化被依赖的Bean并进行依赖注入。如果某个Bean在生成之前要求另一个Bean必须先实例化,则可以指定”depends-on”属性来设置,如果有2个以上的Bean要设置在”depends-on”中,则以都好隔开,例如:

<bean id=”beanOne” class=”XXXXX” depends-on=”beanTwo,beanThree”/>

<bean id=”beanTwo” class=”XXXXX”/>

<bean id=”beanThree” class=”XXXXX”/>

 

l        自动绑定

除了在Bean定义文件中使用<value>指定字符串以及基本类型值,使用<ref>直接指定参考其他Bean的实例,或是使用<bean>标签指定”class”属性,用来指定依赖对象外,Spring也支持隐式的自动绑定。可以通过(byType)或者名称(byName),将某个Bean实例绑定到其他Bean对应的属性上,例如:

public class HelloBean {

    private String name;

    private Date date;

    public HelloBean() {}

    public HelloBean(Date date) { this.date = date; }

    public HelloBean(String name) { this.name = name; }

    public HelloBean(String name, Date date) {

       this.name = name;

       this.date = date;

    }

    public String getName() { return name; }

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

    public Date getDate() { return date; }

    public void setDate(Date date) { this.date = date; }

    public static void main(String args[]) {

       AbstractApplicationContext context =

           new ClassPathXmlApplicationContext("beans.xml");

       HelloBean bean = (HelloBean)context.getBean("helloBean");

       System.out.println("SomeBean->"+bean.getName());

       System.out.println("SomeBean->"+bean.getDate().toString());

    }

}

<bean id="date" class="java.util.Date"/>

<bean id="helloBean" class="com.spring.ch3.HelloBean" autowire="byType">

    <property name="name" value="哈哈呵呵"></property>

</bean>

运行结果为:

SomeBean->哈哈呵呵

SomeBean->Thu Aug 21 13:30:15 CST 2008

在定义文件中,并没有指定”helloBean””date”属性,而是通过自动绑定,在”autowire”属性上指定了”byType”,所以会根据”helloBean”setDate()方法所接受的类型来判断在Bean定义文件中是否定义了累世的类型对象,并将之设置给”helloBean”setDate()。使用自动绑定时,如果”byType”无法完成绑定,则丢出异常。

可以通过指定”byName”来绑定,则Spring会判断Bean定义时的”id”属性上指定的别名与Setter名称是否一致来进行自动绑定。如果使用”byName”无法完成绑定,则对应的Setter仅保持未绑定的状态。

也可以在使用Type3 Dependency Injection时套用自动绑定,也就是在构造方法上也可以尝试自动绑定,实质上是根据byType来绑定,例如:

<bean id="test" class="java.util.Date"/>

<bean id="helloBean" class="com.spring.ch3.HelloBean" autowire="constructor">

<property name="name" value="哈哈呵呵"></property>

</bean>

   如果还想再偷懒,可以设置未”autodetect”,一切都让spring来判断。当”autowire”被设置未”autodected”来处理依赖关系的时候,Spring会先尝试”construtor”,如果没有能建立依赖关系,则在尝试”byType”的方式来建立依赖关系。

       在以上介绍的隐式自动绑定中,由于没有办法从定义文件中清楚看到是否每个属性都完成了设置,为了确定某些依赖关系确实建立,可以建立依赖检查,在<bean>标签使用设置”dependency-check”,可以有4种依赖检查方式:”simple”, “objects”, “all”, “none”

”simple”只检查简单的属性是否完成依赖关系,像是原生(primitive)数据类型或字符串对象;”objects”则检查对象类型的属性是否完成依赖关系;”all”检查全部的属性是否完成依赖关系;”none”是默认值,表示不检查依赖性。例如:

<bean id="test" class="java.util.Date"/>

<bean id="helloBean" class="com.spring.ch3.HelloBean" autowire="constructor" dependency-check="all">

<property name="name" value="哈哈呵呵"></property>

<!—如果该属性没有设置的话,则会有异常 -->

<property name="date" ref="test"></property>

</bean>

 

l        集合对象

对于数组,List, Set, Map等集合对象,在注入前必须填充入一些对象到集合中,然后再将集对象注入

所需要的Bean,也可以由SpringIoc容器来自动维护或者生成结合对象,并完成依赖注入。

    这里直接举个完成的应用程序做示范,例如有个SomeBean类定义,如下所示:

public class SomeBean {

    private String[] someStrArr;

    private Some[] someObjArr;

    private List<Object> someList;

    private Set<Object> someSet;

    private Map<Object, Object> someMap;

    private Properties properties;

    public Properties getProperties() { return properties; }

    public void setProperties(Properties properties) { this.properties = properties; }

    public String[] getSomeStrArr() { return someStrArr; }

    public void setSomeStrArr(String[] someStrArr) { this.someStrArr = someStrArr; }

    public Some[] getSomeObjArr() { return someObjArr; }

    public void setSomeObjArr(Some[] someObjArr) { this.someObjArr = someObjArr; }

    public List<Object> getSomeList() { return someList; }

    public void setSomeList(List<Object> someList) { this.someList = someList; }

    public Map<Object, Object> getSomeMap() { return someMap; }

    public void setSomeMap(Map<Object, Object> someMap) { this.someMap = someMap; }

    public Set<Object> getSomeSet() { return someSet; }

    public void setSomeSet(Set<Object> someSet) { this.someSet = someSet; }

    public static void main(String args[]) {

       AbstractApplicationContext context =

           new ClassPathXmlApplicationContext("beans.xml");

        SomeBean bean = (SomeBean)context.getBean("someBean");

       System.out.println(bean.getSomeList().getClass());

       System.out.println(bean.getSomeObjArr()[0].getName());

       System.out.println(bean.getSomeMap().get("some2"));

       Iterator<Object> iter = bean.getSomeSet().iterator();

       while (iter.hasNext()) {

           System.out.println(iter.next());

       }

       System.out.println(bean.getProperties().get("key1"));

    }

}

<bean id="some1" class="com.spring.ch3.Some">

    <property name="name" value="张三"></property>

</bean>

<bean id="some2" class="com.spring.ch3.Some">

    <property name="name" value="李四"></property>

</bean>

<bean id="someBean" class="com.spring.ch3.SomeBean">

    <property name="someStrArr">

       <list>

           <value>Hello</value>

           <value>Welcome</value>

       </list>

    </property>

    <property name="someObjArr">

       <list>

           <ref bean="some1"/>

           <ref bean="some2"/>

       </list>

    </property>

    <property name="someList">

       <list>

           <value>ListTest</value>

           <ref bean="some1"/>

           <ref bean="some2"/>

       </list>

    </property>

    <property name="someMap">

       <map>

           <entry key="MapTest" value="Hello!张三"/>

           <entry key="someKey1">

              <ref bean="some1"/>

           </entry>

           <entry>

              <key><ref bean="some1"/></key>

              <ref bean="some2"/>

           </entry>

           <entry key-ref="some2" value="some1"></entry>

           <entry key="some2" value="some1"></entry>

       </map>

    </property>

    <property name="someSet">

       <set>

           <value>a set element</value>

           <ref bean="some1"/>

           <ref bean="some1"/>

       </set>

    </property>

    <property name="properties">

       <props>

           <prop key="key1">keyValue1</prop>

           <prop key="key2">keyValue2</prop>

       </props>

    </property>

</bean>

 

如果集合对象不只注入一个对象,则要考虑为集合对象设置”id”名称,例子如下:

Ø         如果是List对象   

<bean id="emailsList" class="org.springframework.beans.factory.config.ListFactoryBean">

<property name="sourceList">

  <list>

      <value>[email protected]</value>

      <value>[email protected]</value>

  </list>

</property>

</bean>

Ø         如果是Map对象

<bean id="emailsMap" class="org.springframework.beans.factory.config.MapFactoryBean">

<property name="sourceMap">

  <map>

      <entry key="a" value="[email protected]"></entry>

      <entry key="b" value="[email protected]"></entry>

  </map>

</property>

</bean>

Ø         如果是Set对象

<bean id="emailsSet" class="org.springframework.beans.factory.config.SetFactoryBean">

  <property name="sourceSet">

      <set>

          <value>[email protected]</value>

          <value>[email protected]</value>

      </set>

  </property>

</bean>

Ø         如果是Properties对象

<bean id="emailsProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">

  <property name="properties">

      <props>

          <prop key="a">[email protected]</prop>

          <prop key="b">[email protected]</prop>

      </props>

  </property>

</bean>

Ø         使用"location"属性,指定.properties文件的位置,从中读取Properties资料

<bean id="businessConfig" class="org.springframework.beans.factory.config.PropertiesFactoryBean">

<property name="location" value="classpath:com/spring/config.properties"></property>

</bean>

Ø         Spring2.0中,还支持集合对象的合并,和Bean的继承类似

<bean id="parent" abstract="true" class="com.spring.ch3.Some">

  <property name="someProperties">

      <props>

          <prop key="key1">keyValue1</prop>

          <prop key="key2">keyValue2</prop>

      </props>

  </property>

</bean>

<bean id="child" parent="parent">

  <property name="someProperties">

      <props merge="true">

          <prop key="key3">keyValue3</prop>

          <prop key="key4">keyValue4</prop>

      </props>

  </property>

</bean>

 

 

l        Spring中的<util>标签

Spring2.0中如果使用基于XML SchemaXML定义文件进行设置,则可以加入新增的<util>标签扩充。

<util>标签在设置Bean定义时更为方便,对于XML配置文件的简化很有帮助,要使用<util>标签,必须在XML中加入util 名称空间(namespace):

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

     xmlns:util="http://www.springframework.org/schema/util"

    xsi:schemaLocation="http://www.springframework.org/schema/beans

    http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

     http://www.springframework.org/schema/util

    http://www.springframework.org/schema/util/spring-util-2.5.xsd "

    default-init-method="init" default-destroy-method="destroy">

   

例如上面的SomeBean的例子可以定义为:

<bean id="some1" class="com.spring.ch3.Some">

    <property name="name" value="张三"></property>

</bean>

<bean id="some2" class="com.spring.ch3.Some">

    <property name="name" value="李四"></property>

</bean>

<util:list id="strArr">

    <value>你们好</value>

    <value>欢迎</value>

</util:list>

<util:list id="objArr">

    <ref bean="some1"/>

    <ref bean="some2"/>

</util:list>

<util:list id="list" list-class="java.util.ArrayList">

    <value>ListTest</value>

    <ref bean="some1"/>

    <ref bean="some2"/>

</util:list>

<util:map id="map" map-class="java.util.HashMap">

    <entry key="mapTest" value="你好!日本"></entry>

    <entry key="someKey1" value="你好!"></entry>

</util:map>

<util:properties id="property">

    <prop key="a">[email protected]</prop>

    <prop key="b">[email protected]</prop>

</util:properties>

<util:set id="set" set-class="java.util.HashSet">

    <value>set1</value>

    <value>set2</value>

</util:set>

<bean id="someBean2" class="com.spring.ch3.SomeBean">

    <property name="someStrArr" ref="strArr"></property>

    <property name="someObjArr" ref="objArr"></property>

    <property name="someList" ref="list"></property>

    <property name="someMap" ref="map"></property>

    <property name="someSet" ref="set"></property>

    <property name="properties" ref="property"></property>

</bean>

   

除了这里介绍的<util>标签之外,还有<util:constant>可用来把Bean的某个成员设置成为其他类的静态变量/常量,而免于设置FieldRetrievingFactoryBean,(ERROR_MESSAGEtestStaticSet赋值)

    <property name="testStaticSet">

       <util:constant static-field="javax.swing.JOptionPane.ERROR_MESSAGE"/>

    </property>

还可以用<util:property-path>标签为某个Bean的属性成员设置”id”名称(也就是说把某个bean的成员拿出来当作单独的bean以供调用),免于设置PropertyPathFactoryBean,例如:

<util:property-path id="testStaticSet" path="someBean2.testStaticSet"/>

调用如下:

System.out.println(">>>"+context.getBean("testStaticSet"));

 

l        Lookup Method Injection

假设现在要设计一个SingletonMessageManager,当调用display()方法时,会取得一个系统新建立的Message对象并显示,例如:

public abstract class MessageManager {

    public void display() {

       Message message = createMessage();

       System.out.println(message); }

    protected abstract Message createMessage();

    public static void main(String args[]) {

       AbstractApplicationContext context =

           new ClassPathXmlApplicationContext("beans.xml");

       MessageManager manger = (MessageManager)context.getBean("messageManager");

       manger.display(); }

}class Message {

    private String sysMessage;

    public Message() { sysMessage = "系统信息: " + new Date().toString(); }

    public String toString() { return sysMessage; }

}

<bean id="sysMessage" class="com.spring.ch3.Message" scope="prototype"></bean>

<bean id="messageManager" class="com.spring.ch3.MessageManager">

    <lookup-method name="createMessage" bean="sysMessage"/>

</bean>

执行结果:

系统信息: Fri Aug 22 11:38:36 CST 2008

 

 

3.3 Bean高级管理

 

l         XML定义文件的配置方式(忽略)

l         Aware相关接口

Spring中提供一些Aware相关接口,实现这些Aware接口的Bean类在被初始之后,可以取得一些Spring所提供的资源或使用某些功能。如:

Ø         org.springframework.beans.factory.BeanNameAware

如果实现了BeanNameAware接口的Bean类,在设置依赖关系之后、初始化方法之前(InitializingBeanafterPropertiesSet()方法或者自定义的init方法),会将Bean在定义文件中的名称通过setBeanName()方法设置给Bean

Ø         org.springframework.beans.factory.BeanFactoryAware

如果实现了BeanFactoryAware接口的Bean类,在设置依赖关系后、初始化方法之前,Spring容器将会注入BeanFactory的实例。

Ø         org.springframework.context.ApplicationContextAware

如果实现了ApplicationContextAware接口的Bean类,在Bean类被初始后,将会被注入ApplicationContext的实例。

Ø         org.springframework.context.ResourceLoaderAware

如果实现了该接口,可以让Bean取得ResourceLoader实例,并进一步取得相关的资源文件。

    像这样用来取得Spring资源的Bean,设计上应该说是Spring框架的一部分,是应用程序与Spring框架的桥梁。

 

l         BeanPostProcessor

Bean的依赖关系由Spring容器建立并设置之后,还有机会定义一些Bean的修正动作来修正相关的属性,方法是让Bean类实现org.springframework.beans.factory.config.BeanPostProcessor接口。(注意:该类会对定义文件中所有的Bean都进行该接口中的方法调用,使用时请小心)

该接口定义了如下的2个方法:

Ø         postProcessBeforeInitialization()

该方法会在Bean类被初始化之前(InitializingBeanafterPropertiesSet()方法或者自定义的init方法)被执行。

Ø         postProcessAfterInitialization()

该方法会在Bean类被初始化之后被执行。

class BeanPostProcessorHelloBean {

     private String helloWord;

     public String getHelloWord() { return helloWord; }

     public void setHelloWord(String helloWord) { this.helloWord = helloWord; }

}

public class BeanPostProcessorExample implements BeanPostProcessor {

     public Object postProcessAfterInitialization(Object bean, String name)

              throws BeansException {

         Field[] fields = bean.getClass().getDeclaredFields();

         for (int i = 0; i < fields.length; i++) {

              Field f = fields[i];

              if (f.getType().equals(String.class)) {

                   try {

                       f.setAccessible(true);

                       String orginal = (String)f.get(bean);

                       f.set(bean, "(" + orginal.toUpperCase()+"->AfterInit)");

                   } catch(Exception e) { System.out.println(e.getMessage()); }

              }

         }

         return bean;

     }

     public Object postProcessBeforeInitialization(Object bean, String name)

              throws BeansException {

         Field[] fields = bean.getClass().getDeclaredFields();

         for (Field f : fields) {

              if (f.getType().equals(String.class)) {

                   f.setAccessible(true);

                   try {

                       String orginal = (String)f.get(bean);

                       System.out.println("-->"+orginal);

                   } catch(Exception e) {}

              }

         }

         return bean;

     }   

     public static void main(String args[]) {

         AbstractApplicationContext context =

              new ClassPathXmlApplicationContext("beans.xml");

         BeanPostProcessorHelloBean bean = (BeanPostProcessorHelloBean)context.getBean("beanPostProcessorHello");

         System.out.println("->"+bean.getHelloWord());

     }

}

<bean id="beanPostProcessorExample" class="com.spring.ch3.BeanPostProcessorExample">

</bean>

<bean id="beanPostProcessorHello" class="com.spring.ch3.BeanPostProcessorHelloBean">

    <property name="helloWord" value="HelloWorld"></property>

</bean>

 

l         BeanFactoryPostProcessor

BeanFactory载入Bean定义文件的所有内容,但还没有正式产生Bean实例前,可以对该BeanFactory进行一些处理,方式是让Bean类实现org.springframework.beans.factory.config.BeanFactoryPostProcessor接口。接口定义的方法如下:

Ø         postProcessBeanFactory()

如果有类SomeBean实现了BeanFactoryPostProcessor接口,则可以在Bean定义文件中定义它,在使用ApplicationContext时,ApplicationContext会自动使用这些类的实例。

Spring中提供有几个BeanFactoryPostProcessor接口的实现类,像

*         org.springframework.beans.factory.config.PropertyPlaceholderConfigurer

*         org.springframework.beans.factory.config.PropertyOverrideConfigurer

*         org.springframework.beans.factory.config.CustomEditorConfigurer

 

l         PropertyPlaceholderConfigurer

通过这个类,可以将一些配置设置信息移出到一个或者多个.property文件中。可以让XML定义文件负责系统的相关设置,而.properties文件可以让客户根据实际应用的需求,自定义一些相关数据(也就是可以在Bean定义文件中直接引用.properties文件中的定义值)

<bean id="configBean" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">

    <property name="location" value="classpath:com/spring/config.properties"></property>

</bean>

<bean id="beanPostProcessorHello" class="com.spring.ch3.BeanPostProcessorHelloBean">

    <property name="helloWord" value="${com.spring.helloword}"></property>

</bean>

 

如果有多个.properties文件,则可以通过“locations”属性来设置,例如:

<bean id="configBean2" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">

    <property name="locations">

       <list>

           <value>classpath:com/spring/config.properties</value>

       </list>

    </property>

</bean>

 

l         CustomEditorConfigurer

这个类实现了接口BeanFactoryPostProcessor。这个类可以读取实现java.beans.PropertyEditor接口的类,并按其中的实现,将字符串值转换为指定类型的对象。

举个例子来说,假设现在设计了两个类:User类和HelloBean类。

<bean id="userHelloBean" class="com.spring.ch3.UserHelloBean">

    <property name="helloWord" value="你好!"></property>

    <property name="user" value="张怡宁,8888"></property>

</bean>

<bean id="configBean"

    class="org.springframework.beans.factory.config.CustomEditorConfigurer">

    <property name="customEditors">

       <map>

           <entry key="com.spring.ch3.User">

              <bean id="userEditor" class="com.spring.ch3.UserEditor"></bean>

           </entry>

       </map>

    </property>

</bean>

class User {

    private String name;

    private int number;

    public String getName() { return name; }

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

    public int getNumber() { return number; }

    public void setNumber(int number) { this.number = number; }

}

class UserHelloBean {

    private String helloWord;

    private User user;

    public String getHelloWord() { return helloWord; }

    public void setHelloWord(String helloWord) { this.helloWord = helloWord; }

    public User getUser() { return user; }

    public void setUser(User user) { this.user = user; }

}

class UserEditor extends PropertyEditorSupport {

    public void setAsText(String text) {

       String[] strs = text.split(",");

       int number = Integer.parseInt(strs[1]);

       User user = new User();

       user.setName(strs[0]+"@-@");

       user.setNumber(number);

       setValue(user);

    }

    public static void main(String args[]) {

       AbstractApplicationContext context =

           new ClassPathXmlApplicationContext("beans.xml");

       UserHelloBean hello = (UserHelloBean)context.getBean("userHelloBean");

       System.out.println(hello.getHelloWord());

       System.out.println("名字: "+hello.getUser().getName());

       System.out.println("数字: "+hello.getUser().getNumber());

    }

}

运行结果:

你好!

名字: 张怡宁@-@

数字: 8888

 

 

 

3.4 资源、消息、事件

l        资源的获得

Spring提供了对资源文件的泛型存取,ApplicationContext继承了org.springframework.core.io.ResourceLoader接口,你可以使用getResource()方法并制定资源文件的URL来取得一个实现Resource接口的实例,例如:

Resource resource = context.getResource(“classpath:admin.properties”);

也可以制定标准的URL,”file:”或者”http:”,例如:

Context.getResource(“file:/workspace/springtest/conf/admin.properties”);

这会回传一个org.springframework.core.io.FileSystemResource的实例,或者你可以按如下来指定回传一个ServletContextResource

Context.getResource(“WEB-INF/conf/admin.properties”);

 

l        解析文字消息

ApplicationContext继承org.springframework.context.MessageSource接口,你可以使用getMessage的方法来取得文件消息的资源文件,从而实现国际化的目的。

Object[] ags = new Object[] {"张怡宁", Calendar.getInstance().getTime()};

System.out.println(context.getMessage("userLogin", ags, Locale.TAIWAN));

System.out.println(context.getMessage("userLogin", ags, Locale.US)); 

<bean id="messageSource"

    class="org.springframework.context.support.ResourceBundleMessageSource">

    <property name="basename" value="message"></property>

</bean>

其中:

message_en_US.properties

userLogin =User {0} login at {1}

message_zh_TW.properties

userLogin =\u7528\u6237 {0} \u4E8E {1} \u767B\u 5F 55

 

运行结果:

用户 张怡宁 2008/8/24 下午 10:56 登录

User 张怡宁 login at 8/24/08 10:56 PM

 

 

l        监听事件

Spring程序执行期间,ApplicationContext本省就会发布一连串的事件,所有发布的事件的抽象类org.springframework.context.ApplicationEvent的子类,例如:

Ø         ContextClosedEvent

ApplicationContext关闭时发布事件。

Ø         ContextRefreshedEvent

ApplicationContext初始或者Refresh时发布事件。

Ø         RequestHandledEvent

Web程序中,当请求被处理时,ApplicationContext会发布此事件。如果你对这些事件有兴趣,则可以实现org.springframework.context.ApplicationContext接口,并在定义文件中定义实现该接口的一个Bean实例:

public interface ApplicationListener extends EventListener {

    void onApplicationEvent(ApplicationEvent event);

}

     

l        事件传播

如果打算发布事件通知ApplicationListener的实例(例如在程序中定义发布事件),可以使用ApplicationContextpublishEvent()方法,如:

class SomeEvent extends ApplicationEvent {

    public SomeEvent(Object source) {

       super(source);

    }

}

 

class SomeListener implements ApplicationListener {

    public void onApplicationEvent(ApplicationEvent event) {

       if (event instanceof SomeEvent) {

           System.out.println(event.getClass());

       }

    }

}

<bean id="consoleListener" class="com.spring.ch3.SomeListener">

</bean>

 

context.publishEvent(new SomeEvent(context));

 

运行结果:

class com.spring.ch3.SomeEvent

 

 



</script>

你可能感兴趣的:(spring学习笔记 第三章)