当前位置: 首页 > news >正文

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、运行结果

我们看一下运行结果:
在这里插入图片描述

总结:

  • 我们在实际中虽然使用的是@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);
    }

运行结果
在这里插入图片描述

扩展:如果当前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);
    }

运行结果为:
在这里插入图片描述

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

我们看其BeanFactory接口,源码如下:
在这里插入图片描述

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

相关文章:

  • 知识蒸馏 | YOLOv7知识蒸馏实战篇 | 2/2*
  • MySQL纯代码复习(上)
  • [附源码]Python计算机毕业设计白果园网上水果超市
  • 【JVM】java的jvm类加载器和类加载子系统
  • 正则表达式(常用最新版)
  • NX二次开发-调内部函数SEL_set_type_filter_index_by_label设置类型过滤器例子剖析怎么查找内部函数调用内部函数
  • 李峋同款爱心代码
  • VMware 网络模式
  • 缓存穿透、缓存击穿、缓存雪崩区别和解决方案
  • SpringBoot--获取路径中的参数(x-www-form-urlencoded)--方法/实例
  • call()、apply()、bind() 区别、使用场景、实现方式
  • python3 爬虫(初试牛刀)
  • excel的frequency函数的用法和实例
  • 程序员这个身份,比你想象的还值钱!
  • Feng Office 3.7.0.5 - 文件上传
  • [译]前端离线指南(上)
  • Angularjs之国际化
  • HashMap ConcurrentHashMap
  • Java面向对象及其三大特征
  • MobX
  • Nacos系列:Nacos的Java SDK使用
  • PermissionScope Swift4 兼容问题
  • PHP CLI应用的调试原理
  • React的组件模式
  • Web设计流程优化:网页效果图设计新思路
  • 力扣(LeetCode)56
  • 猫头鹰的深夜翻译:Java 2D Graphics, 简单的仿射变换
  • 一起参Ember.js讨论、问答社区。
  • 一天一个设计模式之JS实现——适配器模式
  • 移动端唤起键盘时取消position:fixed定位
  • 以太坊客户端Geth命令参数详解
  • 职业生涯 一个六年开发经验的女程序员的心声。
  • 《天龙八部3D》Unity技术方案揭秘
  • 【云吞铺子】性能抖动剖析(二)
  • ​软考-高级-系统架构设计师教程(清华第2版)【第1章-绪论-思维导图】​
  • "无招胜有招"nbsp;史上最全的互…
  • #Js篇:单线程模式同步任务异步任务任务队列事件循环setTimeout() setInterval()
  • (ZT) 理解系统底层的概念是多么重要(by趋势科技邹飞)
  • (九)c52学习之旅-定时器
  • (转)h264中avc和flv数据的解析
  • .libPaths()设置包加载目录
  • .net core 依赖注入的基本用发
  • .NET 将多个程序集合并成单一程序集的 4+3 种方法
  • .Net下的签名与混淆
  • .w文件怎么转成html文件,使用pandoc进行Word与Markdown文件转化
  • @ 代码随想录算法训练营第8周(C语言)|Day53(动态规划)
  • [ 手记 ] 关于tomcat开机启动设置问题
  • [2021ICPC济南 L] Strange Series (Bell 数 多项式exp)
  • [Angular] 笔记 21:@ViewChild
  • [APIO2012] 派遣 dispatching
  • [BUUCTF 2018]Online Tool
  • [BZOJ1877][SDOI2009]晨跑[最大流+费用流]
  • [C#]获取指定文件夹下的所有文件名(递归)
  • [Flex] PopUpButton系列 —— 控制弹出菜单的透明度、可用、可选择状态
  • [hive] 窗口函数 ROW_NUMBER()