BeanFactory 和 FactoryBean 的区别

发布时间 2023-07-13 18:07:44作者: zno2

 

接口

 

用途

 

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(): 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.

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 FactoryBeaninstance 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" />