Spring中BeanFactory与FactoryBean的区别

Spring中BeanFactory与FactoryBean的区别

为了搞明白这个问题,下载了spring源码在本地ide编译调试,debug进去一步步跟踪查看spring的启动流程,运行机制。

之前一直有在小破站看过spring源码的课程,但spring的源码还是太过强大,类库多,各种设计模式灵活运用,有时候一个小的方法跟进去就有好多相关的扩展代码,跟着跟着就偏离了原来的的代码逻辑,方法的调用链很深,幸好看过spring源码的课程讲解,对spring源码的整体启动流程和大体逻辑有所了解,不然看spring源码会懵逼,即便这样,在看debug跟spring源码到具体的一些细节的时候还是会略显吃力,总体来讲还是对spring底层的一些类库和设计的理解有限吧,大体上知道,具体细节到各个具体的类,就还需要再次看代码逻辑

至此,我们切入今天的主题Spring中BeanFactory 和FactoryBean的区别:

先谈谈我个人的理解:

1.这两个类 在spring中都是接口类,属性和方法如下:
在这里插入图片描述

2.由两个接口类的名字和方法可以看出

BeanFactory 是生产Bean实例的一个工厂(Factory),通过这个接口下的方法来获取spring IOC容器中的各种bean实例,在Spring中所有的Bean 都是由 BeanFactory来管理的

FactoryBean 是一个工厂Bean,实际上也是一个Bean 由BeanFactory 创建,只是这个FactoryBean 有点不一样,是一个能生产或修饰对象生成的工厂Bean

个人理解比较浅薄,来抄作业

在网上找找资料总结如下:

一、BeanFactory 接口

beanFactory 接口是spring容器的核心接口,负责:实例化,定位配置应用程序中的对象及建立这些对象间的依赖

spring 提供了多个易用的BeanFactory的实现, 在spring启动中常用的有DefaultListableBeanFactory, AbstractBeanFactory, AbstractAutoCapableBeanFactory

在这里插入图片描述

二、FactoryBean接口

FactoryBean 接口是spring的核心接口,用于编写增强一些逻辑复杂的bean,在bean创建时可以添加逻辑

public interface FactoryBean<T> {
  /**
  * 返回对象的实例
  */
  T getObject() throws Exception;
  /**
  * 返回对象的类型
  */
  Class<?> getObjectType();
  /**
  * 是否是单例
  */   
   boolean isSingleton();
}

简单实现

/**
 * @author xiaolong.ge
 * @since 08 五月 2022
 */
@Component
public class MyFactoryBean implements FactoryBean<UserService> {
    @Override
    public boolean isSingleton() {
        return true;
    }
    @Override
    public UserService getObject() throws Exception {
        System.out.println("MyFactoryBean-->getObject()实例化。。。");
        UserService userService = new UserServiceImpl();
        return userService;
    }
    @Override
    public Class<?> getObjectType() {
        return UserService.class;
    }
}public class AppTest {
    public static void main(String[] args) throws Exception {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(App.class);
        UserService us = (UserService)context.getBean("myFactoryBean");
        System.out.println(us);
        MyFactoryBean myFactoryBean = (MyFactoryBean)context.getBean("&myFactoryBean");
        UserService us2 = myFactoryBean.getObject();
        System.out.println(us2);
        System.out.println(us == us2);
    }
    // 输出结果:
    MyFactoryBean-->getObject()实例化。。。
    com.gexl.service.impl.UserServiceImpl@1440c311
    MyFactoryBean-->getObject()实例化。。。
    com.gexl.service.impl.UserServiceImpl@189b5fb1
    false

增强实现

 /**
 * @author xiaolong.ge
 * @since 08 五月 2022
 */
@Component
public class MyFactoryBean implements FactoryBean<UserService>, InitializingBean, DisposableBean {private Object proxyObject;
    private Object target;
    private String interfaceName;public MyFactoryBean() {
        this.interfaceName = UserService.class.getName();
        this.target = new UserServiceImpl();
    }@Override
    public UserService getObject() throws Exception {
        return (UserService) proxyObject;
    }@Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("myFactoryBean afterPropertiesSet..");
        proxyObject = Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{Class.forName(interfaceName)}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("方法调用之前增强。。。method="+method.getName());
                Object obj = method.invoke(target,args);
                System.out.println("方法调用之后增强。。。method="+method.getName());
                return obj;
            }
        });
    }@Override
    public Class<?> getObjectType() {
        return proxyObject == null ? Object.class : proxyObject.getClass();
    }@Override
    public boolean isSingleton() {
        return true;
    }@Override
    public void destroy() throws Exception {
        System.out.println("destory...");
    }
    public Object getTarget() {
        return target;
    }
    public void setTarget(Object target) {
        this.target = target;
    }
    public String getInterfaceName() {
        return interfaceName;
    }
    public void setInterfaceName(String interfaceName) {
        this.interfaceName = interfaceName;
    }
}public class AppTest {
   
    @Test
    public void testFactoryBean() throws Exception {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(App.class);MyFactoryBean myFactoryBean = (MyFactoryBean) context.getBean("&myFactoryBean");
        System.out.println("myFactoryBean="+myFactoryBean);
        UserService userService = myFactoryBean.getObject();
        System.out.println("userService="+userService);
        
        UserServiceImpl targetService = (UserServiceImpl)myFactoryBean.getTarget();
        String beanName = "userDao";
        UserDao userDao = (UserDao)context.getBean(beanName);
        Field field = targetService.getClass().getDeclaredField(beanName);
        field.setAccessible(true);
        field.set(targetService,userDao);
        // 调用方法
        userService.getById(1);
    }
}
    // 输出结果:
    myFactoryBean afterPropertiesSet..
    myFactoryBean=com.gexl.service.impl.MyFactoryBean@65bb9029
    方法调用之前增强。。。method=toString
    方法调用之后增强。。。method=toString
    userService=com.gexl.service.impl.UserServiceImpl@1bfe3203
    方法调用之前增强。。。method=getById
    方法调用之后增强。。。method=getById

应用实例

Spring在整合mybatis框架时 SqlSessionFactoryBean 就是应用的FactoryBean

Spring会调用SqlSessionFactoryBean这个实现了FactoryBean的工厂Bean 同时加载dataSource,Mapper文件的路径,对sqlSessionFactory进行初始化。

public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
 .....
 @Override
 public void afterPropertiesSet() throws Exception {
   notNull(dataSource, "Property 'dataSource' is required");
   notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
   state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
             "Property 'configuration' and 'configLocation' can not specified with together");
   // 创建 sqlSessionFactory 
   this.sqlSessionFactory = buildSqlSessionFactory();
 }

 /**
  * Build a {@code SqlSessionFactory} instance.
  *
  * The default implementation uses the standard MyBatis {@code XMLConfigBuilder} API to build a
  * {@code SqlSessionFactory} instance based on an Reader.
  * Since 1.3.0, it can be specified a {@link Configuration} instance directly(without config file).
  *
  * @return SqlSessionFactory
  * @throws Exception if configuration is failed
  */
 protected SqlSessionFactory buildSqlSessionFactory() throws Exception {final Configuration targetConfiguration;
   XMLConfigBuilder xmlConfigBuilder = null;
   if (this.configuration != null) {
     targetConfiguration = this.configuration;
     if (targetConfiguration.getVariables() == null) {
       targetConfiguration.setVariables(this.configurationProperties);
     } else if (this.configurationProperties != null) {
       targetConfiguration.getVariables().putAll(this.configurationProperties);
     }
   } else if (this.configLocation != null) {
     xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
     targetConfiguration = xmlConfigBuilder.getConfiguration();
   } else {
     LOGGER.debug(() -> "Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration");
     targetConfiguration = new Configuration();
     Optional.ofNullable(this.configurationProperties).ifPresent(targetConfiguration::setVariables);
   }
   ... 省略
   Optional.ofNullable(this.cache).ifPresent(targetConfiguration::addCache);
   ... 省略
   
   if (this.mapperLocations != null) {
     if (this.mapperLocations.length == 0) {
       LOGGER.warn(() -> "Property 'mapperLocations' was specified but matching resources are not found.");
     } else {
       for (Resource mapperLocation : this.mapperLocations) {
         if (mapperLocation == null) {
           continue;
         }
         try {
           XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
               targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments());
           xmlMapperBuilder.parse();
         } catch (Exception e) {
           throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
         } finally {
           ErrorContext.instance().reset();
         }
         LOGGER.debug(() -> "Parsed mapper file: '" + mapperLocation + "'");
       }
     }
   } else {
     LOGGER.debug(() -> "Property 'mapperLocations' was not specified.");
   }
   return this.sqlSessionFactoryBuilder.build(targetConfiguration);
 }
 ...
 }
三、总结

BeanFactory是Spring中IOC容器最核心的接口,遵循了IOC容器中所需的基本接口。比如:ApplicationContext, AbstractBeanFactory等 都使用了BeanFactory接口。

FactoryBean是工厂类接口,只想简单构造Bean,不希望实现原有大量的方法。它是一个Bean,不过这个Bean能够做为工厂去创建Bean, 同时还能修饰对象的生成

FactoryBean比BeanFactory在生产Bean的时候灵活,还能修饰对象,带有工厂模式和装饰模式的意思在里面,不过它的存在还是以Bean的形式存在

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring框架BeanFactoryFactoryBean是两个重要的概念,虽然名字相近,但它们在功能和作用上有很大的不同。 首先,BeanFactorySpring的容器,是用于管理对象的工厂,负责创建、初始化、装配和管理Bean对象。它是一个顶级接口,Spring框架提供了多个BeanFactory的实现,比如DefaultListableBeanFactory和XmlBeanFactory等。 而FactoryBean是一个工厂Bean接口,它的实现类可以是一个普通的Bean,也可以是一个特殊的Bean。FactoryBean允许我们在Bean创建之前对Bean进行一些配置或者装配处理,从而灵活地管理Bean对象。由于FactoryBean是一个接口,所以我们需要实现它的getObject()方法来返回一个Bean实例。 它们的区别在于: 1. BeanFactorySpring框架的一个顶级接口,它是一个对象容器,用于管理对象的创建、初始化、装配和管理;FactoryBean是一个接口,它用于定制Bean的实例化方式。 2. BeanFactory可以创建和获取多种类型的Bean,包括普通java对象以及Spring特有的Bean对象;而FactoryBean只能够创建一种特殊的Bean类型。 3. BeanFactory是异步创建和初始化Bean对象,以便在需要时才能获取Bean实例;而FactoryBean是同步创建和初始化Bean对象,并将实例化后的Bean对象放入容器以便获取。 4. BeanFactory在获得Bean对象时,需要显式调用getBean()方法才能获取对象;而FactoryBean在获取对象时,直接访问FactoryBean的BeanName即可。 总之,BeanFactorySpring的核心接口之一,它用来管理Bean对象;而FactoryBean是Spring的一个特殊接口,它用来定制Bean的实例化方法,使用上要注意二者的区别

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值