一般情况下,Spring通过反射机制利用bean的class属性指定实现类来实例化bean 。在某些情况下,实例化bean过程比较复杂,如果按照传统的方式,则需要在中提供大量的配置信息,配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案。Spring为此提供了一个
org.Springframework.bean.factory.FactoryBean
的工厂类接口,用户可以通过实现该接口定制实例化bean的逻辑。
FactoryBean
接口对于Spring框架来说占有重要的地位,Spring 自身就提供了70多个FactoryBean的实现。它们隐藏了实例化一些复杂bean的细节,给上层应用带来了便利。从Spring 3.0 开始,FactoryBean
开始支持泛型,即接口声明改为FactoryBean
的形式:
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个方法
T getObject()
:返回由FactoryBean
创建的bean实例,如果isSingleton()
返回true
,则该实例会放到Spring容器中单实例缓存池中。
boolean isSingleton()
:返回由FactoryBean
创建的bean实例的作用域是singleton
还是prototype
。
Class
:返回FactoryBean
创建的bean类型。
当配置文件中
的
class
属性配置的实现类是FactoryBean
时,通过getBean()
方法返回的不是FactoryBean
本身,而是FactoryBean#getObject()
方法所返回的对象,相当于FactoryBean#getObject()
代理了getBean()
方法。Spring提供的工厂Bean,大多数都是以FactoryBean作为后缀,并且大多用于生产一批具有某种特征的Bean实例。
如果希望获取
StudentFactoryBean
的实例,则需要在使用getBean(beanName)
方法时在beanName
前显示的加上&
前缀,例如getBean("&student")
。
PropertyPathFactoryBean
获取其他Bean的属性值PropertyPathFactoryBean
用来获取指定Bean的属性值,获得的值可以注入给其他Bean,也可以直接定义成新的Bean
下面是配置举例,使用了 “Spring简单例子”中的Student
和 Classes
类
<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的唯一标识,
而是用于指定属性表达式的值。这种方式可以简化配置。
在上面的例子中加入下面配置:
<bean id="student.age" class="org.springframework.beans.factory.config.PropertyPathFactoryBean"/>
Integer age = ctx.getBean("student.age", Integer.class);
System.out.println(age);
//--> 输出
//22
除此之外PropertyPathFactoryBean
的setPropertyPath()
还支持复合属性的形式。
即使用下面的方式可以获得班级的名称:
<bean id="classesName" class="org.springframework.beans.factory.config.PropertyPathFactoryBean">
<property name="targetBeanName" value="student"/>
<property name="propertyPath" value="classes.name"/>
bean>
注意: 可以使用
来简化配置,使用该标签需要导入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类中增加静态域CON
public class Classes {
public static String CON = "xdd";
//...
}
<bean id="con" class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean">
<property name="targetClass" value="com.erick.hello.Classes"/>
<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>
FieldRetrievingFactoryBean
与PropertyPathFactoryBean
配置bean的id类似,也可以指定属性表达式的值。
使用该配置可以方便使用内部bean的注入。
<bean id="com.erick.hello.Classes.CON" class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean">
注意: 可以使用
来简化配置,使用该标签需要导入util:
命名空间.
<util:constant static-field="com.erick.hello.Classes.CON" id="CON"/>
MethodInvokingFactoryBean
调用方法获取返回值MethodInvokingFactoryBean
可以调用任意类的类方法,也可以调用任意对象的实例方法,
如果方法有返回值,则即可以将方法的返回值指定为容器的Bean,也可以将返回值注入给其他Bean.
具体的使用方式就不详细介绍了,与上面的两个工厂bean配置方式类似,只是参数不同。
Spring的本质其实是通过XML配置来执行Java代码,因此几乎可以把所有的Java代码放到
Spring的配置文件中管理。
元素配置。setter
方法,使用
元素配置。getter
方法,使用PropertyPathFactoryBean
工厂Bean 或
元素配置。Field
的值,使用FieldRetrievingFactoryBean
工厂Bean 或者
元素配置。MethodInvokingFactoryBean
工厂Bean。注意: 虽然绝大多数java代码可以使用Spring配置,但是千万别这么做,这样过度使用XML配置,
会导致配置文件更加臃肿,难以维护,并会使得程序的可读性严重降低。
通常应该将以下两类信息放到XML中配置