spring讲解七:BeanFactory 与 FactoryBean的区别

本文深入探讨Spring框架中的BeanFactory与FactoryBean概念,解释它们在对象管理和创建中的角色,以及FactoryBean如何用于第三方框架整合,如MyBatis。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

BeanFactory:

理解为一个加工工厂:当程序员把类地址,类别名,类中的属性放在配置文件中后,他就去取这些数据并验证,最终为这些类创建对象。

他的作用就是把所有需要管理的对象存储起来,统一管理,最终提供一个统一的getBean()方法。

如果你模拟过xml方式实现spring的ioc功能,就知道我们把所有需要spring管理的类,都需要配置到xml中;

例如:applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="demoController" class="com.wqm._02annotation.controller.DemoController">
        <property name="demoService" ref="demoService"></property>
    </bean>

    <bean id="demoService" class="com.wqm._02annotation.service.impl.DemoServiceImpl"></bean>

</beans>

如果你模拟过用注解的方式,实现spring的ioc功能,也就会知道注解的作用,其实就只是标识

@Serivce
@Controller
@Component
等等

他们最终都调用了BeanFactory接口,提供的getBean方法;

不管是xml还是注解的方式,他们都是把需要管理的类,先获取到,然后将这些类存储起来,再通过静态代理的方式,为这些类,提供一个统一的方法。而提供统一的规范的这个类,就叫BeanFactory。

下面一段代码展示:BeanFactory的最终作用:(就是spring为了管理对象,写了一个接口,接口的所有实现,都是围绕拿到正确的对象所展开的)

    @org.junit.Test
    public void getOne() {
        /**
         * 方式一: 最原始的方式,获取xml配置,创建对象
         */
        BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:springioc.xml");
        OneController oneController1 = (OneController) beanFactory.getBean("oneController");
        oneController1.getOne();

        /**
         * 方式二: 被封装后的方式,获取xml配置,创建对象
         */
        ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("classpath:springioc.xml");
        OneController oneController = (OneController) classPathXmlApplicationContext.getBean("oneController");
        oneController.getOne();

        /**
         * 方式一: 最原始的方式,获取注解配置,创建对象
         */
        BeanFactory beanFactory = new AnnotationConfigApplicationContext(SpringConfigDemo.class);
        DemoController demoController = (DemoController) beanFactory.getBean("demoController");
        demoController.getDemo();
        
        /**
         * 方式二: 被封装后的方式,获取注解配置,创建对象
         */
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(SpringConfigDemo.class);
        DemoController demoController2 = (DemoController) annotationConfigApplicationContext.getBean("demoController");
        demoController2.getDemo();

    }

现在知道BeanFactory的作用了吧。就是你把对象的位置,名字给我,我就帮你创建一个对象,并把对象的地址放在spring容器中。(spring容器可以理解为一个map集合)

现在好了,比如我把DataServiceImpl这个对象放在了map集合中,我如何来取呢 ?

现在就出来了,@Autowired、@Resource,比如

@Autowired
private DataService dataService;

上面的注解,就标识了,这个类,可以去spring容器中去取(map集合中去取),取map集合中,拿到对象,就可以使用对象了。

说了这么多:BeanFactory就是用来创建对象的。

 

FactoryBean:

这是一个非常重要的知识点,知道这个之后,就可以写mybatis、hibernate、shiro或者其他你自己想写的组件,最终整合spring。

看不懂FactoryBean就看不懂mybatis源码真正的运行过程。

这里我拿mybatis为例,整合spring,并且拿大多数人熟悉的xml配置方式开头讲解

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
	<!-- 数据库连接池 -->
	<property name="dataSource" ref="dataSource" />
	<!-- 加载mybatis的全局配置文件 -->
	<property name="configLocation" value="classpath:mybatis/mybatis-config.xml" />
	<!-- 配置pojo别名 -->
	<property name="typeAliasesPackage" value="com.wqm.step.pojo"/>
</bean>

看这个配置,大家应该都很熟悉,我们要mybaits整合spring,就必须要配置bean:SqlSessionFactoryBean

也就是说,用Bean配置的方式,让spring管理mybatis提供的SqlSessionFactoryBean。

通常情况下,我们自己写的代码,会在需要被spring管理的类上,加上@Component注解,表示这个类会被spring管理。

但是第三方框架,不会直接加上@Component注解,并且第三方框架也不知道我们到底要用它的那些功能。

在我们直接使用第三方框架(有些框架不开源),且不能修改第三方框架的前提下,spring提供了一个技术。

叫 FactoryBean。

下面是mybatis为了整合spring,自己提供的代码


public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
......
}
这段代码我们发现,mybatis提供了一个SqlSessionFactoryBean,并且实现了spring提供的FactoryBean。

其实不光是mybatis,所有可以整合spring的第三方框架,都需要实现FactoryBean。

 

上面的例子证明了,FactoryBean的使用范围,和设计初衷。下面用模拟代码来讲讲spring的FactoryBean的使用

package com.wqm._12FactoryBean;

import com.wqm._12FactoryBean.temp.HelloServiceImpl;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.stereotype.Component;

/**
 * @author: wangqinmin
 * @date : 2020/4/18
 * @description: 仰天大笑出门去,我辈岂是蓬蒿人
 */
@Component
public class DaoFactoryBean implements FactoryBean {
    /**
     * BeanFactory:是用来存储对象的,它提供了一个getBean方法,是用来获取Bean的。
     * FactoryBean:是用来修改的,它可以让你在获取Bean之前,把要获取的Bean修改成其他的Bean
     * <p>
     * 正常来说,我们可以在Test类中,获取DaoFactoryBean这个类,并可以直接调用这里面的下面方法。
     *
     */
    public void test() {
        System.out.println("DaoFactoryBean  的   test方法执行  ");
    }


    /**
     * 这个方法可以获取对象,可以理解为获取Bean
     *
     * @return
     * @throws Exception
     */
    public Object getObject() throws Exception {
        return new HelloServiceImpl();
    }


    /**
     * 返回对象类型
     *
     * @return
     */
    public Class<?> getObjectType() {
        return HelloServiceImpl.class;
    }


    /**
     * 设置对象是否单例
     *
     * @return
     */
    public boolean isSingleton() {
        return true;
    }
}

我自定义了一个DaoFactoryBean,并且实现了FactoryBean

下面测试:

package com.wqm._12FactoryBean;

import com.wqm._12FactoryBean.temp.HelloServiceImpl;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * @author: wangqinmin
 * @date : 2020/4/15
 * @description: 仰天大笑出门去,我辈岂是蓬蒿人
 */
public class Test {

    /**
     * FactoryBean的作用:
     */
    @org.junit.Test
    public void test() {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
        DaoFactoryBean daoFactoryBean = (DaoFactoryBean) applicationContext.getBean("daoFactoryBean");
        /**
         * java.lang.ClassCastException: com.wqm._12FactoryBean.temp.HelloServiceImpl cannot be cast to com.wqm._12FactoryBean.DaoFactoryBean
         *
         * 为什么呢 ?
         * 因为DaoFactoryBean实现了FactoryBean;
         *
         * spring源码写到: 如果实现FactoryBean,那么获取这个类的Bean,就必须加上&符号。
         * 否则,就是获取这个类中,实现getObject()方法和getObjectType()方法的的那个类的Bean
         */
        daoFactoryBean.test();
    }
}

结果是报错了,类类型转换异常。上面的注释说明了原因。

所以我们需要将上面的

DaoFactoryBean daoFactoryBean = (DaoFactoryBean) applicationContext.getBean("daoFactoryBean");

改为:

DaoFactoryBean daoFactoryBean = (DaoFactoryBean) applicationContext.getBean("&daoFactoryBean");

多加一个&符号:结果是调用成功。

那我们没有加&符号的时候,applicationContext.getBean("daoFactoryBean");

返回类型,到底是什么呢 ?

我们在看看上面

public class DaoFactoryBean implements FactoryBean{}

这个类,实现了FactoryBean后,重写三个方法。

其中有两个方法,一个是getObject(),一个是getObjectType(),重写的时候,我就指定了spring要管理 DaoFactoryBean的时候,实际上是管理一个叫HelloServiceImpl的类。

所以进行如下测试:

package com.wqm._12FactoryBean;

import com.wqm._12FactoryBean.temp.HelloServiceImpl;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * @author: wangqinmin
 * @date : 2020/4/15
 * @description: 仰天大笑出门去,我辈岂是蓬蒿人
 */
public class Test {

    /**
     * FactoryBean的作用:
     */
    @org.junit.Test
    public void test() {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
        HelloServiceImpl helloServiceImpl = (HelloServiceImpl) applicationContext.getBean("daoFactoryBean");
        helloServiceImpl.hello();
    }
}

结果是成功的。

 

结论:FactoryBean是用来指定外部某个类,被spring创建对象(管理对象)。

 

完工。

疑问:  (其实这是我自己的疑问,不过我一轮测试下来,发现了问题及原因的所在,还是对spring基础了解不够,不过也很开心,至少知道问题出在什么地方,并知道怎么解决。)

疑问来源于,为什么第三方框架必须实现FactoryBean,难道不能直接用@Bean注解,强行将第三方框架中的类交给spring管理吗?

比如,拿JavaConfig方式举例:

1. 要让spring管理,必须在spring的扫描包下。这时候,我把mybatis的包也扫描到。

2. 要让spring管理,在JavaConfig下用注解@Bean,将这类初始化。

上面两步都没有问题。但是有一个问题,spring管理的类,都有一个类似别名的东西,也就是spring的ioc功能。

比如:
@Service

public void DemoService(){}

没有ioc的加持,我们怎么来DI ?所以还是会报错。

这样下来,就必须在被管理的类上加 @Component。

由于是第三方框架,所以必须下载源码,重新编译,并加上 @Component注解

 

结论: 饭吃饱了。

 

spring的3种使用方式:

 

1. xml配置,管理对象

2. xml配置+开启注解,管理对象

3. javaConfig配置+注解,管理对象

 

用第一种方式,确实可以解决上面的问题,因为没有注解,直接配置就好了。

但2、3种方式,是不能解决的。

但是第1、2种方式在springboot坐江山的时候,已经被淘汰了。

 

上面那个疑问不成立的原因就是:

spring管理Bean的过程,有4个步骤;

1. 包扫描Bean

2. 初始化实例化Bean

3. 管理Bean(ioc)

4. 应用过程: 依赖注入(di)

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值