【Spring】——8、如何使用FactoryBean向Spring容器中注册bean?_后端

📫作者简介:​​zhz小白​​ 公众号:​小白的Java进阶之路​​
专业技能:
1、Java基础,并精通多线程的开发,熟悉JVM原理
2、熟悉Java基础,并精通多线程的开发,熟悉JVM原理,具备⼀定的线上调优经验
3、熟悉MySQL数据库调优,索引原理等,⽇志原理等,并且有出过⼀篇专栏
4、了解计算机⽹络,对TCP协议,滑动窗⼝原理等有⼀定了解
5、熟悉Spring,Spring MVC,Mybatis,阅读过部分Spring源码
6、熟悉SpringCloud Alibaba体系,阅读过Nacos,Sentinel,Seata,Dubbo,Feign,Gateway核⼼源码与设计,⼆次开发能⼒
7、熟悉消息队列(Kafka,RocketMQ)的原理与设计
8、熟悉分库分表ShardingSphere,具有真实⽣产的数据迁移经验
9、熟悉分布式缓存中间件Redis,对其的核⼼数据结构,部署架构,⾼并发问题解决⽅案有⼀定的积累
10、熟悉常⽤设计模式,并运⽤于实践⼯作中
11、了解ElasticSearch,对其核⼼的原理有⼀定的了解
12、了解K8s,Jekins,GitLab
13、了解VUE,GO
14、⽬前有正在利⽤闲暇时间做互游游戏,开发、运维、运营、推销等

本人著作git项目:​​https://gitee.com/zhouzhz/star-jersey-platform​​,有兴趣的可以私聊博主一起编写,或者给颗star
领域:​​对支付(FMS,FUND,PAY),订单(OMS),出行行业等有相关的开发领域​​
🔥如果此文还不错的话,还请👍关注、点赞、收藏三连支持👍一下博主~

文章目录

  • ​​1、定义​​
  • ​​2、源码​​
  • ​​2.1、分析​​
  • ​​2.1.1、T getObject()​​
  • ​​2.1.2、Class<?> getObjectType()​​
  • ​​2.1.3、boolean isSingleton()​​
  • ​​2.2、案例​​
  • ​​2.2.1、手写FactoryBean的实现类​​
  • ​​2.2.2、在MainConfig注册TestFactoryBean​​
  • ​​2.2.3、运行结果​​
  • ​​2.2.4、疑问:怎么获取FactoryBean对象本身?加&​​
  • ​​2.4.5、为什么加了&之后就能获取FactoryBean对象实例呢?​​

1、定义

  • 一般情况下,Spring是通过反射机制利用bean的class属性指定实现类来实例化bean的。在某些情况下,实例化bean过程比较复杂,如果按照传统的方式,那么则需要在标签中提供大量的配置信息,配置方式的灵活性是受限的,这时采用编码的方式可以得到一个更加简单的方案。Spring为此提供了一个org.springframework.bean.factory.FactoryBean的工厂类接口,用户可以通过实现该接口定制实例化bean的逻辑。
  • 简单理解为我们可以自己创造Bean,不需要通过BeanPostProcessor来干涉Spring创建bean的过程(factorybean创建的bean只会经过初始化后,其他Spring的生命周期不会经过)

2、源码

/*
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

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 to
* other beans, implement {@link BeanFactoryAware} and obtain them programmatically.
*
* <p><b>The container is only responsible for managing the lifecycle of the FactoryBean
* instance, not the lifecycle of the objects created by the FactoryBean.</b> Therefore,
* a destroy method on an exposed bean object (such as {@link java.io.Closeable#close()})
* will <i>not</i> be called automatically. Instead, a FactoryBean should implement
* {@link DisposableBean} and delegate any such close call to the underlying object.
*
* <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;
}

}

当配置文件中标签的class属性配置的实现类是FactoryBean时,通过 getBean()方法返回的不是FactoryBean本身,而是FactoryBean#getObject()方法所返回的对象,相当于FactoryBean#getObject()代理了getBean()方法。

2.1、分析

2.1.1、T getObject()

  • 返回由FactoryBean创建的bean实例,如果isSingleton()返回true,那么该实例会放到Spring容器中单实例缓存池中

2.1.2、Class<?> getObjectType()

  • 返回FactoryBean创建的bean实例的类型

2.1.3、boolean isSingleton()

  • 返回由FactoryBean创建的bean实例的作用域是singleton还是prototype

2.2、案例

2.2.1、手写FactoryBean的实现类

我们写一个TestFactoryBean类,它实现了FactoryBean接口,代码如下:

package com.zhz.factorybean;

import com.zhz.bean.UserService;
import org.springframework.beans.factory.FactoryBean;

/**
* 创建一个Spring定义的FactoryBean
* T(泛型):指定我们要创建什么类型的对象
* @author zhouhengzhe
* @date 2022/11/18
*/
public class TestFactoryBean implements FactoryBean<UserService> {

/**
* 返回一个UserService对象,这个对象会添加到容器中
*/
@Override
public UserService getObject() throws Exception {
return new UserService();
}

@Override
public Class<?> getObjectType() {
// 返回这个对象的类型
return UserService.class;
}

/**
* 是单例吗?
* 如果返回true,那么代表这个bean是单实例,在容器中只会保存一份;
* 如果返回false,那么代表这个bean是多实例,每次获取都会创建一个新的bean
*
* @return
*/
@Override
public boolean isSingleton() {
return true;
}
}

其中里面的UserService代码如下:

package com.zhz.bean;

/**
* @author zhouhengzhe
* @date 2022/11/18
*/
public class UserService {
}

2.2.2、在MainConfig注册TestFactoryBean

然后我们在MainConfig中注册TestFactoryBean,此处我们用的是@Bean向Spring容器注册对象,代码如下

@Bean
public TestFactoryBean testFactoryBean(){
return new TestFactoryBean();
}

然后我们运行一下test方法,代码如下:

@Test
public void test3(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
Object colorFactoryBean = applicationContext.getBean("testFactoryBean");
System.out.println("bean的类型为:"+colorFactoryBean);
}

2.2.3、运行结果

我们看一下运行结果:

【Spring】——8、如何使用FactoryBean向Spring容器中注册bean?_spring_02

总结:

  • 我们在实际中虽然使用的是@Bean注解向容器中注册的是TestFactoryBean对象,但是确是调用的getObject()方法,对象类型是getObject的返回值

2.2.4、疑问:怎么获取FactoryBean对象本身?加&

我么只需要在获取Bean的时候,加上&符号,那么他就会获取FactoryBean本身了,比如**&testFactoryBean**

@Test
public void test3(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
Object colorFactoryBean = applicationContext.getBean("&testFactoryBean");
System.out.println("bean的类型为:"+colorFactoryBean);
}

运行结果

【Spring】——8、如何使用FactoryBean向Spring容器中注册bean?_sed_03

扩展:如果当前Bean不是FactoryBean的实现类,也加了&,那么会报错,具体样例如下:如下我们的person就是一个跟简单的Bean类

@Test
public void test3(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
Object colorFactoryBean = applicationContext.getBean("&testFactoryBean");
Object person = applicationContext.getBean("&person");
System.out.println("bean的类型为:"+colorFactoryBean);
System.out.println("bean的类型为:"+person);
}

运行结果为:

【Spring】——8、如何使用FactoryBean向Spring容器中注册bean?_后端_04

2.4.5、为什么加了&之后就能获取FactoryBean对象实例呢?

我们看其BeanFactory接口,源码如下:

【Spring】——8、如何使用FactoryBean向Spring容器中注册bean?_sed_05

  • 在BeanFactory接口中定义了一个**&**前缀,只要我们使用bean的id来从Spring容器中获取bean时,Spring就会知道我们是在获取FactoryBean本身。