4.3通过FactoryBean配置bean

来源:互联网 发布:梅兰商城源码 编辑:程序博客网 时间:2024/05/24 01:08

FactoryBean介绍

一般情况下,Spring通过反射机制利用bean的class属性指定实现类来实例化bean 。在某些情况下,实例化bean过程比较复杂,如果按照传统的方式,则需要在中提供大量的配置信息,配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案。Spring为此提供了一个org.Springframework.bean.factory.FactoryBean的工厂类接口,用户可以通过实现该接口定制实例化bean的逻辑。

FactoryBean接口对于Spring框架来说占有重要的地位,Spring 自身就提供了70多个FactoryBean的实现。它们隐藏了实例化一些复杂bean的细节,给上层应用带来了便利。从Spring 3.0 开始, FactoryBean开始支持泛型,即接口声明改为FactoryBean<T> 的形式:

package com.erick.d1.hello;import org.springframework.beans.factory.FactoryBean;public class StudentFacotryBean implements FactoryBean<Student>{    private String stuName;    @Override    public Student getObject() throws Exception {        Student t = new Student();        t.setName(stuName);        return t;    }    @Override    public Class<?> getObjectType() {        return Student.class;    }    @Override    public boolean isSingleton() {        return false;    }    //getter and setter ...}
<bean id="student" class="com.erick.d1.hello.StudentFacotryBean">    <property name="stuName" value="stu-factoryBean"/></bean> 

接口中定义了3个方法

  1. T getObject():返回由FactoryBean创建的bean实例,如果isSingleton()返回true,则该实例会放到Spring容器中单实例缓存池中。

  2. boolean isSingleton():返回由FactoryBean创建的bean实例的作用域是singleton还是prototype

  3. Class<T> getObjectType():返回FactoryBean创建的bean类型。

当配置文件中<bean>class属性配置的实现类是FactoryBean时,通过 getBean()方法返回的不是FactoryBean本身,而是FactoryBean#getObject()方法所返回的对象,相当于FactoryBean#getObject()代理了getBean()方法。

Spring提供的工厂Bean,大多数都是以FactoryBean作为后缀,并且大多用于生产一批具有某种特征的Bean实例。

如果希望获取StudentFactoryBean的实例,则需要在使用getBean(beanName) 方法时在beanName前显示的加上 & 前缀,例如getBean("&student")

Spring中一些FactoryBean的使用介绍

使用PropertyPathFactoryBean获取其他Bean的属性值

PropertyPathFactoryBean用来获取指定Bean的属性值,获得的值可以注入给其他Bean,也可以直接定义成新的Bean

下面是配置举例,使用了 “Spring简单例子”中的StudentClasses

<bean id="student" class="com.erick.hello.Student" p:age = "22" p:name = "cat">    <property name="classes">        <bean class="com.erick.hello.Classes" p:name="软件三班" p:number="333"/>    </property></bean><bean id="classes" class="org.springframework.beans.factory.config.PropertyPathFactoryBean">    <property name="targetBeanName" value="student"/>    <property name="propertyPath" value="classes"/></bean>
ApplicationContext ctx = new ClassPathXmlApplicationContext("Beans.xml");Classes classes= ctx.getBean("classes", Classes.class);Student student= ctx.getBean("student", Student.class);System.out.println(classes);System.out.println(student.getClasses());System.out.println(student.getClasses() == classes);//--> 输出//Classes{name='软件三班', number=333}//Classes{name='软件三班', number=333}//true

上面例子中通过PropertyPathFactoryBean获取到了student中的classes
这个bean可以单独使用,也可以注入到其他bean中。

注意: 上面配置PropertyPathFactoryBean的bean标签中的id属性,并不是该Bean的唯一标识,
而是用于指定属性表达式的值。这种方式可以简化配置。

在上面的例子中加入下面配置:

<!-- 表示取得student实例的age属性 --><bean id="student.age" class="org.springframework.beans.factory.config.PropertyPathFactoryBean"/>
Integer age = ctx.getBean("student.age", Integer.class);System.out.println(age);//--> 输出//22

除此之外PropertyPathFactoryBeansetPropertyPath()还支持复合属性的形式。
即使用下面的方式可以获得班级的名称:

<bean id="classesName" class="org.springframework.beans.factory.config.PropertyPathFactoryBean">    <property name="targetBeanName" value="student"/>    <property name="propertyPath" value="classes.name"/></bean>

注意: 可以使用<util:property-path>来简化配置,使用该标签需要导入util:命名空间.

<!-- 获取学生所在班级的名字 --><util:property-path id="classesName1" path="student.classes.name"/>

注意: 通过PropertyPathFactoryBean来获取其他Bean的属性值时,必须要求该属性有相应的getter方法,
否则会抛出异常。

使用FieldRetrievingFactoryBean获取Field值

FieldRetrievingFactoryBean可以访问静态类的静态Field或者对象实例的Field值。
其中访问对象实例的Field在实际编程中没有什么用处,因为实际中java类的Field大多用private修饰,
并使用getter和setter来访问和修改,而FieldRetrievingFactoryBean要求实例Field用public修饰

//在Classes类中增加静态域CONpublic class Classes {    public static String CON = "xdd";    //...}
<bean id="con" class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean">    <!-- targetClass和targetObject选择一个配置即可。用来指定访问哪个目标类 -->    <property name="targetClass" value="com.erick.hello.Classes"/>    <!-- <property name="targetObject" ref="classes"/> -->    <!-- 指定要访问的Field -->    <property name="targetField" value="CON"/></bean>

也可以直接使用staticField指定访问哪个类的哪个静态域

<bean id="con" class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean">    <property name="staticField" value="com.erick.hello.Classes.CON"/></bean>

FieldRetrievingFactoryBeanPropertyPathFactoryBean配置bean的id类似,也可以指定属性表达式的值
使用该配置可以方便使用内部bean的注入。

<bean id="com.erick.hello.Classes.CON" class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean">

注意: 可以使用<util:constant>来简化配置,使用该标签需要导入util:命名空间.

<util:constant static-field="com.erick.hello.Classes.CON" id="CON"/>

使用MethodInvokingFactoryBean调用方法获取返回值

MethodInvokingFactoryBean可以调用任意类的类方法,也可以调用任意对象的实例方法,
如果方法有返回值,则即可以将方法的返回值指定为容器的Bean,也可以将返回值注入给其他Bean.

具体的使用方式就不详细介绍了,与上面的两个工厂bean配置方式类似,只是参数不同。

总结

Spring的本质其实是通过XML配置来执行Java代码,因此几乎可以把所有的Java代码放到
Spring的配置文件中管理。

  • 调用构造器创建对象,包括使用工厂方法创建对象,使用<bean ../>元素配置。
  • 调用setter方法,使用<property .../>元素配置。
  • 调用getter方法,使用PropertyPathFactoryBean工厂Bean 或<util:property-path .../>元素配置。
  • 获取Field的值,使用FieldRetrievingFactoryBean工厂Bean 或者<util:constant .../>元素配置。
  • 调用普通方法,使用MethodInvokingFactoryBean工厂Bean。

注意: 虽然绝大多数java代码可以使用Spring配置,但是千万别这么做,这样过度使用XML配置,
会导致配置文件更加臃肿,难以维护,并会使得程序的可读性严重降低。

通常应该将以下两类信息放到XML中配置

  • 项目升级和维护时,经常改动的信息。
  • 控制项目内各个组件耦合关系的代码。
0 0
原创粉丝点击