首页 > 其他分享 >Spring容器系列-FactoryBean使用/原理

Spring容器系列-FactoryBean使用/原理

时间:2024-06-19 12:35:04浏览次数:27  
标签:容器 Spring beanName bean FactoryBean CustomerFactoryBean UserService

Spring- FactoryBean使用/原理

    概要

    在某些情况下,实例化Bean过程比较复杂,若按照传统的方式,则需要在中提供大量的配置信息,不够灵活,这时采用编码的方式能得到一个简单的方案。
    Spring为此提供了一个org.springframework.bean.factory.FactoryBean的工厂类接口,用户可以通过实现该接口定制实例化Bean的逻辑。

    FactoryBean接口在Spring中占重要地位,Spring自身就提供了70多个Factory Bean的实现。它们隐藏了实例化一些复杂Bean的细节,给上层应用带来了便利。

    从Spring3.0开始,FactoryBean开始支持泛型,即接口声明改为FactoryBean<T>的形式。

    一、普通bean与FactoryBean的区别

   Spring容器中有两种bean:普通bean和工厂bean。

   1. 普通bean

  通过反射来实例化被标记为bean的类。例:@Component指定的类。

   2. FactoryBean

   FactoryBean返回的对象不是指定类的一个实例,而是Factory Bean#getObject方法返回的对象。若想获取Factory Bean本身,要在bean的名称添加前缀&来获取FactoryBean对象本身(applicationContext.getBean("&"+beanName)。

   FactoryBean接口源码:

 1 public interface FactoryBean<T>{
 2    String OBJECT_TYPE_ATTRIBUTE "factoryBeanObjectType";
 3 
 4    @Nullable
 5    T getobject()throws Exception;
 6 
 7    @Nullable
 8    Class<?>getobjectType();
 9 
10    default boolean isSingleton(){
11       return true;
12    }
13 }

   3. FactoryBean不遵循Spring的生命周期

   Spring的作者想要放权给使用者,让使用者自己实现创建一个bean的逻辑,所以Spring并不会过多的插手该Bean的实例化过程,使得一个Bean的实例化完全由使用者本人去实现。

   这个类并不会像普通bean那样在Spring容器初始化时进行实例化,而是类似于懒加载,在获取时才进行创建和返回。至于是不是单例,要取决于isSingleton()方法的返回值。

   二 、FactoryBean的用法

   FactoryBean的特殊之处在于它可以向容器中注册两个Bean,一个是它本身,一个是FactoryBean.getObject()方法返回值所代表的Bean。先通过如下示例代码来感受下FactoryBean的用处吧。

   自定义一个类CustomerFactoryBean,让它实现了FactoryBean接口,重写了接口中的两个方法,在getObejct()方法中,返回了一个UserService的实例对象;在getObjectType()方法中返回了UserService.class。

   然后在CustomerFactoryBean添加了注解@Component注解,意思是将CustomerFactoryBean类交给Spring管理。

   UserService类:

1 public class UserService {
2     public UserService() {
3         System.out.println("userService construct");
4     }
5 }

 

   CustomerFactoryBean类:

 1 package org.example.factory;
 2 
 3 import org.example.service.UserService;
 4 import org.springframework.beans.factory.FactoryBean;
 5 import org.springframework.stereotype.Component;
 6 
 7 /**
 8  * @author hxq
 9  * @date 2024/6/19 11:07
10  */
11 @Component
12 public class CustomerFactoryBean  implements FactoryBean<UserService> {
13     public UserService getObject() throws Exception {
14         return new UserService();
15     }
16 
17     public Class<?> getObjectType() {
18         return UserService.class;
19     }
20 }

   AppConfig类:

1 @Configuration
2 @ComponentScan("org.example.factory")
3 public class AppConfig {
4 
5 }

  启动类:

 1 public class App {
 2     public static void main(String[] args) {
 3         AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
 4         System.out.println("容器启动完成");
 5         CustomerFactoryBean rawBean = (CustomerFactoryBean) applicationContext.getBean("&customerFactoryBean");
 6         System.out.println(rawBean);
 7 
 8         UserService userService = applicationContext.getBean(UserService.class);
 9         System.out.println(userService);
10         Object customerFactoryBean = applicationContext.getBean("customerFactoryBean");
11         System.out.println(customerFactoryBean);
12     }
13 }
14 
15 # 运行结果
16 容器启动完成
17 org.example.factory.CustomerFactoryBean@35fc6dc4
18 userService construct
19 org.example.service.UserService@1dd92fe2
20 org.example.service.UserService@1dd92fe2

   从上面例子可以看出,FactoryBean是一个特殊的Bean。我们自定义的CustomerFactoryBean实现了FactoryBean接口,所以当CustomerFactoryBean被扫描进Spring容器时,实际上它向容器中注册了两个bean,一个是CustomerFactoryBean类的单例对象;另外一个就是getObject()方法返回的对象。

   三、FactoryBean的源码

   通过上面的示例代码,我们知道了FactoryBean的作用,也知道该如何使用FactoryBean,那么接下来我们就通过源码来看看FactoryBean的工作原理。

   在Spring容器启动阶段,会调用到refresh()方法,在refresh()中有调用了finishBeanFactoryInitialization()方法,最终会调用到beanFactory.preInstantiateSingletons()方法。所以我们先看下这个方法的源码。

   DefaultListableBeanFactory#preInstantiateSingletons方法:

 1 public void preInstantiateSingletons() throws BeansException {
 2     // 从容器中获取到所有的beanName
 3     List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
 4     for (String beanName : beanNames) {
 5         RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
 6         if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
 7 
 8             // 在此处会根据beanName判断bean是不是一个FactoryBean,实现了FactoryBean接口的bean,会返回true
 9             // 此时当beanName为customerFactoryBean时,会返回true,会进入到if语句中
10             if (isFactoryBean(beanName)) {
11                 // 然后通过getBean()方法去获取或者创建单例对象
12                 // 注意:在此处为beanName拼接了一个前缀:FACTORY_BEAN_PREFIX
13                 // FACTORY_BEAN_PREFIX是一个常量字符串,即:&
14                 // 所以在此时容器启动阶段,对于customerFactoryBean,应该是:getBean("&customerFactoryBean")
15                 Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
16 
17 
18                 // 下面这一段逻辑,是判断是否需要在容器启动阶段,就去实例化getObject()返回的对象,即是否调用FactoryBean的getObject()方法
19                 if (bean instanceof FactoryBean) {
20                     final FactoryBean<?> factory = (FactoryBean<?>) bean;
21                     boolean isEagerInit;
22                     if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
23                         isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
24                                         ((SmartFactoryBean<?>) factory)::isEagerInit,
25                                 getAccessControlContext());
26                     }
27                     else {
28                         isEagerInit = (factory instanceof SmartFactoryBean &&
29                                 ((SmartFactoryBean<?>) factory).isEagerInit());
30                     }
31                     if (isEagerInit) {
32                         getBean(beanName);
33                     }
34                 }
35             }
36             else {
37                 getBean(beanName);
38             }
39         }
40     }
41 }

 

 

 

 

  参考链接:https://juejin.cn/post/6844903954615107597

标签:容器,Spring,beanName,bean,FactoryBean,CustomerFactoryBean,UserService
From: https://www.cnblogs.com/hld123/p/18255964

相关文章

  • SpringBoot整合JWT(JSON Web Token)生成token与验证
    目录JWT什么是JWTJWT使用流程确定要传递的信息:生成JWT:JWT传输:客户端保存JWT:客户端发送JWT:服务器验证JWT:服务器响应:Token的使用示例:工具类R结果集返回一个生成的token创建拦截器JWT什么是JWTJWT(JSONWebToken)是是目前最流行的跨域认证解决方案。它通常被......
  • 【计算机毕业设计】springboot电影院订票信息管理系统
    当今社会已经步入了科学技术进步和经济社会快速发展的新时期,国际信息和学术交流也不断加强,计算机技术对经济社会发展和人民生活改善的影响也日益突出,人类的生存和思考方式也产生了变化。传统电影院订票采取了人工的管理方法,但这种管理方法存在着许多弊端,比如效率低下、安全......
  • Asp.net Core依赖注入(Autofac替换IOC容器)
    ASP.NETCoreASP.NETCore(previouslyASP.NET5)改变了以前依赖注入框架集成进ASP.NET的方法.以前,每个功能-MVC,WebAPI,等.-都有它自己的"依赖解析器(dependencyresolver)"机制并且只是'钩子'钩住的方式有些轻微的区别.ASP.NETCore通过Microsoft.Extensions.......
  • 【Spring Cloud应用框架】
    ......
  • 基于SpringBoot+Vue+uniapp的高校实验室信息化综合管理平台建设的详细设计和实现(源码
    文章目录前言详细视频演示具体实现截图技术栈后端框架SpringBoot前端框架Vue持久层框架MyBaitsPlus系统测试系统测试目的系统功能测试系统测试结论为什么选择我代码参考数据库参考源码获取前言......
  • 基于SpringBoot+Vue+uniapp的电商购物网站的详细设计和实现(源码+lw+部署文档+讲解等)
    文章目录前言详细视频演示具体实现截图技术栈后端框架SpringBoot前端框架Vue持久层框架MyBaitsPlus系统测试系统测试目的系统功能测试系统测试结论为什么选择我代码参考数据库参考源码获取前言......
  • 基于SpringBoot+Vue+uniapp的酒店预订管理系统的详细设计和实现(源码+lw+部署文档+讲
    文章目录前言详细视频演示具体实现截图技术栈后端框架SpringBoot前端框架Vue持久层框架MyBaitsPlus系统测试系统测试目的系统功能测试系统测试结论为什么选择我代码参考数据库参考源码获取前言......
  • 【Spring】Bean管理
    获取Bean要从IOC容器当中来获取到bean对象,需要先拿到IOC容器对象@AutowiredprivateApplicationContextapplicationContext;//IOC容器对象Spring容器中提供了一些方法,可以主动从IOC容器中获取到bean对象,下面介绍3种常用方式:根据name获取beanObjectgetBean(Stringna......
  • 【Spring】AOP
    AOP英文全称:AspectOrientedProgramming(面向切面编程、面向方面编程)。在程序运行期间在不修改源代码的基础上对已有方法进行增强(无侵入性:解耦)AOP快速入门pom.xml<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</......
  • springboot 使用 doris-streamloader 到doris 防止批量更新 事务卡主
    背景:使用mybatis批量实时和更新doris时经常出现连接不上的错误,导致kafka死信队列堆积很多滞后消费https://doris.apache.org/zh-CN/docs/2.0/ecosystem/doris-streamloader/packagecom.jiaoda.sentiment.data.etl.service.update;importcn.hutool.core.text.CharSequenc......