7.8.3 容器中的工厂Bean
此处的工厂Bean,与前面介绍的实例工厂方法创建Bean,或者静态工厂方法创建Bean的工厂有所区别:前面那些工厂是标准的工厂模式, Spring只是负责调用工厂方法来创建Bean实例;此处的工厂Bean是Spring的一种特殊Bean,这种工厂Bean必须实现FactoryBean接口。
FactoryBean接口是工厂Bean的标准接口,把工厂Bean(实现FactoryBean接口的Bean)部署在容器中之后,如果程序通过getBean()方法来获取它时,容器返回的不是FactoryBean实现类的实例,而是返回FactoryBean的产品(即该工厂Bean的getObject()方法的返回值)
FactoryBean接口提供如下三个方法。
FactoryBean接口方法 |
描述 |
T getObject() |
该方法负责返回该工厂Bean生成的Java实例。 |
Class<?> getObjectType() |
该方法返回该工厂Bean生成的Java实例的类型。 |
boolean isSingleton() |
该方法用于判断该工厂Bean生成的Java实例是否为单例模式。 |
配置FactoryBean与配置普通Bean的定义没有区别,但当程序向Spring容器请求获取该Bean时,容器返回该FactoryBean的产品,而不是返回该FactoryBean本身。
从上面介绍不难发现,实现FactoryBean接口的最大作用在于:Spring容器并不是简单地返回该Bean的实例,而是返回该Bean实例的getObject()方法的返回值,而getObject()方法则由开发者负责实现,这样开发者希望Spring返回什么,只要按需求重写getObject()方法即可。
程序示例
项目结构
1 2 3 4 5 6 7 8 9 10
| E:\workspace_QingLiangJiJavaEEQiYeYingYongShiZhang5\GetFieldFactoryBean └─src\ ├─beans.xml ├─lee\ │ └─SpringTest.java └─org\ └─crazyit\ └─app\ └─factory\ └─GetFieldFactoryBean.java
|
GetFieldFactoryBean.java
下面定义了一个标准的工厂Bean,这个工厂Bean实现了FactoryBean接口。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| package org.crazyit.app.factory;
import java.lang.reflect.*; import org.springframework.beans.factory.FactoryBean;
public class GetFieldFactoryBean implements FactoryBean<Object> { private String targetClass; private String targetField; public void setTargetClass(String targetClass) { this.targetClass = targetClass; }
public void setTargetField(String targetField) { this.targetField = targetField; } public Object getObject() throws Exception { Class<?> clazz = Class.forName(targetClass); Field field = clazz.getField(targetField); return field.get(null); } public Class<? extends Object> getObjectType() { return Object.class; } public boolean isSingleton() { return false; } }
|
上面的GetFieldFactoryBean是一个标准的工厂Bean,该工厂Bean的关键代码就在所实现的getObect()方法,该方法的执行体先使用反射获取targetClass对应的Class对象,再获取targetField对应的类变量的值。 GetFieldFactoryBean的targetClass、 targetField都提供了setter方法,因此可接受Spring的设值注入,这样即可让GetFieldFactoryBean获取指定类的、指定静态Field的值。
由于程序不需要让GetFieldFactoryBean的getObject()方法产生的值是单例的,故该工厂类的isSingleton()方法返回false.
下面配置文件将使用GetFieldFactoryBean来获取指定类的、指定静态Field的值。
beans.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| <?xml version="1.0" encoding="GBK"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="north" class="org.crazyit.app.factory.GetFieldFactoryBean"> <property name="targetClass" value="java.awt.BorderLayout"/> <property name="targetField" value="NORTH"/> </bean>
<bean id="theValue" class="org.crazyit.app.factory.GetFieldFactoryBean"> <property name="targetClass" value="java.sql.ResultSet"/> <property name="targetField" value="TYPE_SCROLL_SENSITIVE"/> </bean> </beans>
|
从上面的程序可以看出,部署工厂Bean与部署普通Bean其实没有任何区别,同样只需为该Bean配置id、 class两个属性即可,但Spring对FactoryBean接口的实现类的处理有所不同。
Spring容器对工厂Bean的处理过程
Spring容器会自动检测容器中的所有Bean,如果发现某个Bean实现类实现了FactoryBean接口,Spring容器就会在实例化该Bean、根据<property>执行setter方法之后,额外调用该Bean的getObject()方法,并将该方法的返回值作为容器中的Bean.
下面程序示范了获取容器中的FactoryBean的产品。
SpringTest.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| package lee;
import org.springframework.context.*; import org.springframework.context.support.*;
public class SpringTest { public static void main(String[] args)throws Exception { @SuppressWarnings("resource") ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); System.out.println(ctx.getBean("north")); System.out.println(ctx.getBean("theValue")); System.out.println(ctx.getBean("&theValue")); } }
|
上面程序main方法中前两行输出代码,直接请求容器中的FactoryBean, Spring将不会返回该FactoryBean本身,而是返回该FactoryBean的产品;
程序中的第三行输出代码代码在Bean id前增加&符号,这将会让Spring返回FactoryBean本身。
测试
编译、运行该程序,可以看到如下输出:
1 2 3
| North 1005 org.crazyit.app.factory.GetFieldFactoryBean@78c03f1f
|
从上面三行输出可以看出,使用该GetFieldFactoryBean即可让程序自由获取任意类的、任意静态Field的值。实际上, Spring框架本身提供了一个FieldRetrievingFactoryBean,这个FactoryBean与我们自己实现的GetFieldFactoryBean具有基本相同的功能,7.10节会详细介绍FieldRetrievingFactoryBean的功能和用法。
实际上, FactoryBean是Spring中非常有用的一个接口, Spring内置提供了很多实用的工厂Bean,例如TransactionProxyFactoryBean等,这个工厂Bean专门用于为目标Bean创建事务代理。
Spring提供的工厂Bean,大多以Factory Bean后缀结尾,并且大多用于生产一批具有某种特征的Bean实例,工厂Bean是Spring的一个重要工具类.