BeanFactory 和 FactoryBean 的区别
接口
用途
package org.springframework.scripting; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Main { public static void Main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); Calculator calc = (Calculator) ctx.getBean("calculator"); System.out.println(calc.add(2, 8)); } }
<bean id="defaultCacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"> <property name="configLocation"> <value>WEB-INF/spring/ehcache.xml</value> </property> </bean> <bean id="ehCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean"> <property name="cacheManager"> <ref local="defaultCacheManager"/> </property> <property name="cacheName"> <value>DEFAULT_CACHE</value> </property> </bean>
分析
BeanFactory 是bean工厂,是bean上下文 ,生产各种bean。
7.16 The BeanFactory The BeanFactory provides the underlying basis for Spring’s IoC functionality but it is only used directly in integration with other third-party frameworks and is now largely historical in nature for most users of Spring. The BeanFactory and related interfaces, such as BeanFactoryAware, InitializingBean, DisposableBean, are still present in Spring for the purposes of backward compatibility with the large number of third-party frameworks that integrate with Spring. Often third-party components that can not use more modern equivalents such as @PostConstruct or @PreDestroy in order to remain compatible with JDK 1.4 or to avoid a dependency on JSR-250. This section provides additional background into the differences between the BeanFactory and ApplicationContext and how one might access the IoC container directly through a classic singleton lookup.
FactoryBean 是一种初始化很复杂的bean,需要用到复杂java逻辑,生产单一bean。
举个例子,在xml中想要配置某个bean ,只能用setter 和 constructor ,java代码没法写。
7.8.3 Customizing instantiation logic with a FactoryBean Implement the org.springframework.beans.factory.FactoryBean interface for objects that are themselves factories. The FactoryBean interface is a point of pluggability into the Spring IoC container’s instantiation logic. If you have complex initialization code that is better expressed in Java as opposed to a (potentially) verbose amount of XML, you can create your own FactoryBean, write the complex initialization inside that class, and then plug your custom FactoryBean into the container. The FactoryBean interface provides three methods: Object getObject(): returns an instance of the object this factory creates. The instance can possibly be shared, depending on whether this factory returns singletons or prototypes. boolean isSingleton(): returns true if this FactoryBean returns singletons, false otherwise. Class getObjectType(): returns the object type returned by the getObject() method or null if the type is not known in advance. The FactoryBean concept and interface is used in a number of places within the Spring Framework; more than 50 implementations of the FactoryBean interface ship with Spring itself. When you need to ask a container for an actual FactoryBean instance itself instead of the bean it produces, preface the bean’s id with the ampersand symbol ( &) when calling the getBean() method of the ApplicationContext. So for a given FactoryBean with an id of myBean, invoking getBean("myBean") on the container returns the product of the FactoryBean; whereas, invoking getBean("&myBean") returns the FactoryBean instance itself.
查阅文档
The FactoryBean
interface provides three methods:
Object getObject()
: returns an instance of the object this factory creates. The instance can possibly be shared, depending on whether this factory returns singletons or prototypes.boolean isSingleton()
: returnstrue
if thisFactoryBean
returns singletons,false
otherwise.Class getObjectType()
: returns the object type returned by thegetObject()
method ornull
if the type is not known in advance.
When you need to ask a container for an actual FactoryBean
instance itself instead of the bean it produces, preface the bean’s id with the ampersand symbol ( &
) when calling the getBean()
method of the ApplicationContext
. So for a given FactoryBean
with an id of myBean
, invoking getBean("myBean")
on the container returns the product of the FactoryBean
; whereas, invoking getBean("&myBean")
returns the FactoryBean
instance itself.
举个例子
└─foo Foo.java foo.xml FooFactoryBean.java FooFactoryBeanTest1.java FooFactoryBeanTest2.java
Foo.java
package foo; public class Foo { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }
FooFactoryBean.java
package foo; import org.springframework.beans.factory.FactoryBean; public class FooFactoryBean implements FactoryBean<Foo> { private String name; @Override public Foo getObject() throws Exception { Foo foo = new Foo(); foo.setName(name); return foo; } @Override public Class<?> getObjectType() { return Foo.class; } @Override public boolean isSingleton() { return false; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
foo.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="foo" class="foo.FooFactoryBean" > <property name="name" value="sssss"></property> </bean> </beans>
FooFactoryBeanTest1.java
package foo; import static org.junit.Assert.assertTrue; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @ContextConfiguration(locations = { "classpath*:foo/foo.xml" }) @RunWith(SpringJUnit4ClassRunner.class) public class FooFactoryBeanTest1 { @Autowired private Foo foo; @Test public void ff() { assertTrue("sssss".equals(foo.getName())); } }
FooFactoryBeanTest2.java
package foo; import static org.junit.Assert.assertTrue; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class FooFactoryBeanTest2 { @Test public void ff() { ApplicationContext ac = new ClassPathXmlApplicationContext("foo/foo.xml"); Object fooFactoryBean = ac.getBean("&foo"); Object foo = ac.getBean("foo"); assertTrue(fooFactoryBean instanceof FooFactoryBean); assertTrue(foo instanceof Foo); if(foo instanceof Foo) { assertTrue("sssss".equals(((Foo)foo).getName())); } // 非单例,所以再次获取是不同的对象 assertTrue(foo != ac.getBean("foo")); } }
FooFactoryBeanTest3.java
package foo; import static org.junit.Assert.assertTrue; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class FooFactoryBeanTest3 { @Test public void ff() { ApplicationContext ac = new ClassPathXmlApplicationContext("foo/foo.xml"); // 当FooFactoryBean 的 isSingleton 方法返回true时,单例,所以再次获取是相同对象 assertTrue(ac.getBean("foo") == ac.getBean("foo")); } }
托管bean的另一种方式
package cn.xs.wo; public class Foo { public A getA() { return new A(); } public B getB() { return new B(); } }
package cn.xs.wo; public class A { }
package cn.xs.wo; public class B { }
package cn.xs.wo; import org.springframework.beans.factory.FactoryBean; public class FooFactoryBean implements FactoryBean<Foo>{ @Override public Foo getObject() throws Exception { return new Foo(); } @Override public Class<?> getObjectType() { return Foo.class; } }
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="foo" class="cn.xs.wo.FooFactoryBean"></bean> <bean id="a" factory-bean="foo" factory-method="getA"></bean> <bean id="b" factory-bean="foo" factory-method="getB"></bean> </beans>
package cn.xs.wo; import org.junit.Test; import org.springframework.context.support.GenericXmlApplicationContext; public class FooApplicationTest { @Test public void test() { GenericXmlApplicationContext ac = new GenericXmlApplicationContext("foo.xml"); Foo foo = ac.getBean(Foo.class); assert(foo != null); A a = ac.getBean(A.class); assert(a != null); B b = ac.getBean(B.class); assert(b != null); ac.close(); } }
实际运用:
org.activiti.spring.ProcessEngineFactoryBean
<bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean"> <property name="processEngineConfiguration" ref="processEngineConfiguration" /> </bean> <bean id="repositoryService" factory-bean="processEngine" factory-method="getRepositoryService" /> <bean id="runtimeService" factory-bean="processEngine" factory-method="getRuntimeService" /> <bean id="taskService" factory-bean="processEngine" factory-method="getTaskService" /> <bean id="historyService" factory-bean="processEngine" factory-method="getHistoryService" /> <bean id="managementService" factory-bean="processEngine" factory-method="getManagementService" />