7. spring-容器:浅谈FactoryBean

先总结一下目前向spring容器注册Bean主要是以下几种方式:

  • @ComponentScan + @Service, @Component etc…
  • @Configuration + @Bean
  • @Import: javabean, ImportSelector, ImportBeanDefinitionRegistrar

注册的Bean往往提供了class路径,然后spring容器利用反射机制生成目标bean对象。但有些场景下实例化bean较复杂,期望可以通过编码的方式定制bean。此时Spring容器提供了支持,这些特殊的bean可实现FactoryBean接口。

FactoryBean的源码定义如下:

package org.springframework.beans.factory;

import org.springframework.lang.Nullable;

/**
 * Interface to be implemented by objects used within a {@link BeanFactory} which
 * are themselves factories for individual objects. If a bean implements this
 * interface, it is used as a factory for an object to expose, not directly as a
 * bean instance that will be exposed itself.
 *
 * <p><b>NB: A bean that implements this interface cannot be used as a normal bean.</b>
 * A FactoryBean is defined in a bean style, but the object exposed for bean
 * references ({@link #getObject()}) is always the object that it creates.
 *
 * <p>FactoryBeans can support singletons and prototypes, and can either create
 * objects lazily on demand or eagerly on startup. The {@link SmartFactoryBean}
 * interface allows for exposing more fine-grained behavioral metadata.
 *
 * <p>This interface is heavily used within the framework itself, for example for
 * the AOP {@link org.springframework.aop.framework.ProxyFactoryBean} or the
 * {@link org.springframework.jndi.JndiObjectFactoryBean}. It can be used for
 * custom components as well; however, this is only common for infrastructure code.
 *
 * <p><b>{@code FactoryBean} is a programmatic contract. Implementations are not
 * supposed to rely on annotation-driven injection or other reflective facilities.</b>
 * {@link #getObjectType()} {@link #getObject()} invocations may arrive early in
 * the bootstrap process, even ahead of any post-processor setup. If you need access
 * other beans, implement {@link BeanFactoryAware} and obtain them programmatically.
 *
 * <p>Finally, FactoryBean objects participate in the containing BeanFactory's
 * synchronization of bean creation. There is usually no need for internal
 * synchronization other than for purposes of lazy initialization within the
 * FactoryBean itself (or the like).
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @since 08.03.2003
 * @param <T> the bean type
 * @see org.springframework.beans.factory.BeanFactory
 * @see org.springframework.aop.framework.ProxyFactoryBean
 * @see org.springframework.jndi.JndiObjectFactoryBean
 */
public interface FactoryBean<T> {

	/**
	 * The name of an attribute that can be
	 * {@link org.springframework.core.AttributeAccessor#setAttribute set} on a
	 * {@link org.springframework.beans.factory.config.BeanDefinition} so that
	 * factory beans can signal their object type when it can't be deduced from
	 * the factory bean class.
	 * @since 5.2
	 */
	String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";


	/**
	 * Return an instance (possibly shared or independent) of the object
	 * managed by this factory.
	 * <p>As with a {@link BeanFactory}, this allows support for both the
	 * Singleton and Prototype design pattern.
	 * <p>If this FactoryBean is not fully initialized yet at the time of
	 * the call (for example because it is involved in a circular reference),
	 * throw a corresponding {@link FactoryBeanNotInitializedException}.
	 * <p>As of Spring 2.0, FactoryBeans are allowed to return {@code null}
	 * objects. The factory will consider this as normal value to be used; it
	 * will not throw a FactoryBeanNotInitializedException in this case anymore.
	 * FactoryBean implementations are encouraged to throw
	 * FactoryBeanNotInitializedException themselves now, as appropriate.
	 * @return an instance of the bean (can be {@code null})
	 * @throws Exception in case of creation errors
	 * @see FactoryBeanNotInitializedException
	 */
	@Nullable
	T getObject() throws Exception;

	/**
	 * Return the type of object that this FactoryBean creates,
	 * or {@code null} if not known in advance.
	 * <p>This allows one to check for specific types of beans without
	 * instantiating objects, for example on autowiring.
	 * <p>In the case of implementations that are creating a singleton object,
	 * this method should try to avoid singleton creation as far as possible;
	 * it should rather estimate the type in advance.
	 * For prototypes, returning a meaningful type here is advisable too.
	 * <p>This method can be called <i>before</i> this FactoryBean has
	 * been fully initialized. It must not rely on state created during
	 * initialization; of course, it can still use such state if available.
	 * <p><b>NOTE:</b> Autowiring will simply ignore FactoryBeans that return
	 * {@code null} here. Therefore it is highly recommended to implement
	 * this method properly, using the current state of the FactoryBean.
	 * @return the type of object that this FactoryBean creates,
	 * or {@code null} if not known at the time of the call
	 * @see ListableBeanFactory#getBeansOfType
	 */
	@Nullable
	Class<?> getObjectType();

	/**
	 * Is the object managed by this factory a singleton? That is,
	 * will {@link #getObject()} always return the same object
	 * (a reference that can be cached)?
	 * <p><b>NOTE:</b> If a FactoryBean indicates to hold a singleton object,
	 * the object returned from {@code getObject()} might get cached
	 * by the owning BeanFactory. Hence, do not return {@code true}
	 * unless the FactoryBean always exposes the same reference.
	 * <p>The singleton status of the FactoryBean itself will generally
	 * be provided by the owning BeanFactory; usually, it has to be
	 * defined as singleton there.
	 * <p><b>NOTE:</b> This method returning {@code false} does not
	 * necessarily indicate that returned objects are independent instances.
	 * An implementation of the extended {@link SmartFactoryBean} interface
	 * may explicitly indicate independent instances through its
	 * {@link SmartFactoryBean#isPrototype()} method. Plain {@link FactoryBean}
	 * implementations which do not implement this extended interface are
	 * simply assumed to always return independent instances if the
	 * {@code isSingleton()} implementation returns {@code false}.
	 * <p>The default implementation returns {@code true}, since a
	 * {@code FactoryBean} typically manages a singleton instance.
	 * @return whether the exposed object is a singleton
	 * @see #getObject()
	 * @see SmartFactoryBean#isPrototype()
	 */
	default boolean isSingleton() {
		return true;
	}

}

从源码注释可以分析出:

  • 这是Spring刚诞生起就有的特性,作者是Rod Johnson。

  • 实现FactoryBean的Bean,最后从容器中取出时会使用工厂方法生成对象,而不是bean本身。

  • FactoryBean能支持singleton或prototype,能支持lazy加载或急加载。SmartFactoryBean接口能支持更细粒度的控制

  • 这个接口在spring框架中被重度使用。如支持AOP的ProxyFactoryBean, 支持JNDI的JndiObjectFactoryBean。当然自定义组件也可以使用,一般用于基础设施。

  • getObjectType()和getObject()的在启动阶段的早期执行,在post-processor之前。

  • 可通过实现BeanFactoryAware接口获取到当前容器引用。

  • 从spring3.0开始,接口支持范型形式

三个方法的介绍:

T getObject() throws Exception;

返回由FactoryBean创建的Bean实例,如果isSingleton()返回true,则会被spring容器缓存供复用

Class<?> getObjectType();

返回创建Bean的Class类型

boolean isSingleton();

返回bean的作用范围,是单例还是prototype。

示例

自定义一个FactoryBean

package win.elegentjs.spring.ioc.factorybeans;

import org.springframework.beans.factory.FactoryBean;
import win.elegentjs.spring.ioc.beans.Person;

/**
 * 自定义的FactoryBean, 返回Person实例
 */
public class PersonFactoryBean implements FactoryBean<Person> {
    @Override
    public Person getObject() {
        return new Person("zhangsan", 20);
    }

    @Override
    public Class<?> getObjectType() {
        return Person.class;
    }
}

默认是单例

定义java config类来加载上面的FactoryBean

package win.elegentjs.spring.ioc.factorybeans;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 自定义的FactoryBeanConfig,加载factoryBean至容器
 *
 * 从容器中通过getBean获取到的对象不是PersonFactoryBean本身,
 * 而是PersonFactoryBean的getObject()方法返回的对象
 *
 */
@Configuration
public class PersonFactoryBeanConfig {

    @Bean
    public PersonFactoryBean personBean() {
        return new PersonFactoryBean();
    }
}

虽然定义的bean的返回值类型是PersonFactoryBean, 但真正从容器中根据“personBean” bean name取出并不是PersonFactoryBean类型,而是getobject返回的类型

看测试结果

package win.elegentjs.spring.ioc.factorybeans;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import win.elegentjs.spring.ioc.beans.Person;
import win.elegentjs.spring.ioc.imports.enables.EnableOrgConfig;
import win.elegentjs.spring.util.ArraysUtil;

@Slf4j
public class PersonFactoryBeanSample {

    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(PersonFactoryBeanConfig.class);
        Person p = (Person)context.getBean("personBean");
        PersonFactoryBean factoryBean = (PersonFactoryBean)context.getBean("&personBean");

        log.info(p.toString());
        log.info(factoryBean.toString());
    }
}

// result:
2021-05-25 13:57:06.885 [main] INFO  w.e.s.ioc.factorybeans.PersonFactoryBeanSample-Person(name=zhangsan, age=20)
2021-05-25 13:57:06.889 [main] INFO  w.e.s.ioc.factorybeans.PersonFactoryBeanSample-win.elegentjs.spring.ioc.factorybeans.PersonFactoryBean@6adbc9d

可以看出跟Bean name取出的bean对象类型是Person而不是PersonFactoryBean。另外想取出FactoryBean本身的bean也不难,是bean name前面拼接上“&”号即可。(从容器中取出FactoryBean本身不常见,了解即可)

小结

FactoryBean本身理解起来和用起来并不难,学好它由利用于研究源码实现和自定义FactoryBean组件。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值