Spring 工厂方法与FactoryBean(实例化Bean)

该篇博客关于工厂方法与FactoryBean,其实就是在Spring中不再使用Spring创建Bean实例,而是利用工厂方法与FactoryBean把Bean创建过程转移到开发者手中

该篇博客目录

1、理解工厂方法和FactoryBean是干什么的
2、静态工厂方法实例化Bean
3、工厂方法实例化Bean
4、FactoryBean实例化Bean(工厂Bean)

一、理解工厂方法和FactoryBean是干什么的

笔者认为这一块在本篇博客是最重要的,你得理解工厂方法和FactoryBean是干什么的,以及它和哪些是同类

首先通常在Spring中,我们会利用XML或者注解的方式类配置bean。而在XML方式中有三种方式来实例化bean
  • 反射模式
  • 工厂方法模式
  • FactoryBean模式

其中经常会用到反射模式,即在bean的配置中,声明bean的全类名,而本博客笔者将会介绍工厂模式和FactoryBean模式下实例化bean,对两种模式深入理解其思想


二、静态工厂方法实例化Bean(Static Factory Method)

考虑一个场景,在Spring中将一个单例类配置Bean。一般来说,单例类的实例只能通过静态工厂方法创建(笔者这里在《Spring IN ACTION》的例子上进行扩展):以下例子一个舞台类,舞台只有一个,即采用静态工厂方法


Stage.java

package com.linjie.staticfactory;

/**
 * @author LinJie
 * @Description:这是一个舞台单例类(所以需要静态工厂方法)
 * 每个参赛者用同一个舞台
 */
public class Stage {
    private static Stage instance;
    public static Stage getInstance() {
        if(instance == null){
            instance = new Stage();
        }
        return instance;
    }

    //搭建舞台完成
    public void CreateStageSuccess() {
        System.out.println("舞台搭建完成,舞者可以上台了");
    }
}

Dancer.java(舞者类)

package com.linjie.staticfactory;
/**
 * @author LinJie
 * @Description:这是每个舞者的类,但他们使用同一个舞台
 */
public class Dancer {
    private Stage stage;

    /**
     * @param stage the stage to set
     */
    public void setStage(Stage stage) {
        this.stage = stage;
    }

    //LinJie dancer上台
    public void Show() {
        stage.CreateStageSuccess();
        System.out.println("LinJie dancer上台");
    }
}

测试类

package com.linjie.staticfactory;
/**
 * @author LinJie
 * @Description:这是每个舞者的类,但他们使用同一个舞台
 */
public class Dancer {
    private Stage stage;

    /**
     * @param stage the stage to set
     */
    public void setStage(Stage stage) {
        this.stage = stage;
    }

    //LinJie dancer上台
    public void Show() {
        stage.CreateStageSuccess();
        System.out.println("LinJie dancer上台");
    }
}

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 -->
    <bean id="stage" class="com.linjie.staticfactory.Stage" factory-method="getInstance"></bean>

    <bean id="dancer" class="com.linjie.staticfactory.Dancer">
        <property name="stage" ref="stage"></property>
    </bean> 

</beans>

结果

舞台搭建完成,舞者可以上台了
LinJie dancer上台

注意:Stage没有公开的构造方法,取而代之的是,静态方法getInstance()每次调用都返回相同的实例(因为舞台只有一个),那么在Spring中如何把没有公开构造方法的Stage配置为一个Bean呢?

可以看到在applicationContext.xml中看到

  • factory-method:允许我们调用一个指定的静态工厂方法,从而代替构造方法来创建一个类的实例(即指定静态工厂方法名)
  • class:不再是Bean实例的实现类,而是Bean实例的静态工厂类(上面的例子没有体现这一点,下面的例子根据这一点来实现)
  • 如果静态工厂方法需要参数,使用为其配置

Stage.java(一个舞台类,可传名字)

package com.linjie.staticfactory;
/**
 * @author LinJie
 * @Description:一个舞台类,可以给舞台赋值(其实这里我只想演示传参)
 */
public class Stage {
    private String name;

    /**
     * @param name the name to set
     */
    public void setName(String name) {
        this.name = name;
    }

    //搭建舞台
    public void Show() {
        System.out.println(name+"舞台搭建完成");
    }
}

staticfactory.java(静态工厂方法实现类)

package com.linjie.staticfactory;

/**
 * @author LinJie
 * @Description:这是一个静态工厂方法的类,用于创建单例舞台
 */
public class staticfactory {
    private static Stage instance;
    public static Stage getInstance() {
        if(instance == null){
            instance = new Stage();
        }
        return instance;
    }
}

测试类

package com.linjie.staticfactory;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class staticFactoryBean {
    @Test
    public void test() {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Stage stage = (Stage) context.getBean("stage");
        stage.Show();
    }
}

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 -->
    <!-- class:这里不再是Bean实例的实现类,而是生成Bean实例的静态工厂类 -->
    <bean id="stage" class="com.linjie.staticfactory.staticfactory" factory-method="getInstance">
        <property name="name" value="haha"></property>
    </bean> 
</beans>

结果

这里写图片描述

注意:这里的class属性的值不是Bean实例的实现类,而是生成Bean实例的静态工厂类


三、工厂方法实例化Bean(Instance Factory Method)

ABean.java(一个Bean实例的实现类)

package com.linjie.factory;

/**
 * @author LinJie
 * @Description
 */
public class ABean {
    //利用工厂方法实例化Bean成功显示
    public void show() {
        System.out.println("工厂方法实例化Bean成功");
    }
}

FactoryBean.java(工厂方法)

package com.linjie.factory;

/**
 * @author LinJie
 * @Description:工厂方法实例化Bean
 */
public class FactoryBean {
    public ABean getABean() {
        return new ABean();
    }
}

测试类

package com.linjie.factory;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class FactoryTest {
    @Test
    public void test() {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        ABean abean = (ABean) context.getBean("abean");
        abean.show();
    }
}

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 -->
    <bean id="factorybean" class="com.linjie.factory.FactoryBean"></bean>

    <bean id="abean" factory-bean="factorybean" factory-method="getABean"></bean> 

</beans>

结果

这里写图片描述

在applicationContext的属性中

  • factory-bean:指定工厂方法所在的工厂类实例(即工厂方法bean的id,用法与ref类似)
  • factory-method:还是指定工厂方法名
  • 也可以通过来指定方法调用参数

四、FactoryBean实例化Bean

FactoryBean是Spring容器提供的一种可以扩展容器对象实例化逻辑的接口,请不要将其与容器名称BeanFactory相混淆。FactoryBean,其主语是Bean,定语是Factory,也就是说,它本身与其他注册到容器的对象一样,只是一个Bean而已,只不过这里类型的Bean本身就是生产对象的工厂

应用场景

当某些对象的实例话过程过于烦琐,通过XML配置过于复杂,使我们宁愿使用Java代码来完成这个实例化过程的时候,或者,某些第三方库不能直接注册到Spring容器中的时候,就可以实现org.spring-framework.beans.factory.FactoryBean接口,给出自己的对象实例化代码。当然实现自定义工厂也是可以的。但是FactoryBean是Spring的标准

使用方法

1、创建FactoryBean的实现类

2、实现以下三个方法

- public String getObject() throws Exception:该方法返回该FactoryBean“生产”的对象。我们需要实现该方法以给出自己对象实例化逻辑 
- public Class<?> getObjectType():该方法仅返回getObject()方法所返回的对象的类型。如果预先无法确定,则返回null
- public boolean isSingleton() :该方法返回结果用于表明,getObject()“生产”的对象是否要以singleton(单例)形式存于容器中。如果以singleton形式存在,则返回true,否则返回false

nameFactoryBean实现类

package com.linjie.factorybean;

import org.springframework.beans.factory.FactoryBean;

public class nameFactoryBean<T> implements FactoryBean<String> {
    //该方法返回该FactoryBean“生产”的对象
    //我们需要实现该方法以给出自己对象实例化逻辑
    @Override
    public String getObject() throws Exception {
        return new String("linjie");
    }

    //该方法仅返回getObject()方法所返回的对象的类型
    //如果预先无法确定,则返回null
    @Override
    public Class<?> getObjectType() {
        return null;
    }

    //该方法返回结果用于表明,getObject()“生产”的对象是否要以singleton(单例)形式存于容器中
    //如果以singleton形式存在,则返回true,否则返回false
    @Override
    public boolean isSingleton() {
        return false;
    }

}

测试类

package com.linjie.factorybean;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class factorybeanTest {
    @Test
    public void test() {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        String bean = (String) context.getBean("bean");
        System.out.println(bean);
    }
}

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">  

     <!-- FactoryBean实例化Bean -->
     <bean id="bean" class="com.linjie.factorybean.nameFactoryBean"></bean> 

</beans>

结果

这里写图片描述

FactoryBean和BeanFactory区别

  • FactoryBean:就是笔者上文阐述的,以Bean结尾,表示它是个Bean,它并不是简单的Bean,而是一个能生产对象或者修饰对象的工厂Bean
  • BeanFactory:它是Spring IoC容器的一种形式,提供完整的IoC服务支持,也可以看出主语是Factory,即是一个管理Bean的工厂,关于BeanFactory可以转战笔者另一篇博客https://blog.csdn.net/w_linux/article/details/80025048

参考

《Spring揭秘》

《Spring IN ACTION》

©️2020 CSDN 皮肤主题: 程序猿惹谁了 设计师:上身试试 返回首页