【Spring源码解析】FactoryBean-工厂方法模式的实现及使用

一、工厂方法模式中的三种模式的特点

工厂模式中的三种模式,分别是:简单工厂模式、工厂方法模式、抽象工厂模式,三种分别是什么,以及适合场景是什么?

(1)简单工厂模式:一个抽象接口对应一个产品接口,特定产品实现这个接口,针对不同产品都可以在同一个工厂中生产,同一个工厂生产产品可以通过多种方式,单生产方法(通过类型判定具体是要哪个产品,并进行new返回),多生产方法(工厂中有多个产品的不同生产方法,每一个生产方法对应一个new 产品的return),或者是静态方法简单工厂(将工厂中的返回某个产品的方法类设置为static,则直接通过类调用静态方法即可,不需要new工厂类的对象实例)

该方法的问题是:如果要新增一个产品,必须对工厂类进行修改,但是如果通过反射或者其他方式可以做统一处理,其实也不会涉及到这个问题,如果要新增一个产品必须要对工厂类修改的时候,此时针对简单工厂模式的修改觉得比较麻烦的话或者想要使扩展性更好的话,可以通过工厂方法模式(备注:简单工厂模式比较像一个集中厂,各种不同的东西都能产)

Client端去调用的时候,需要知道:工厂的类名,及提供的生产方法从而可以获得具体产品的实例,以及产品的接口,可以通过实例调用接口得到需要的产品的信息

其实就是:一个工厂类名Fatory,从工厂的生产方法获取具体产品实例,getProduct()其中会返回new ProductA()或者new ProductB(),之后想要真正去进行产品的使用,还需要通过产品的接口,进行调用,例如ProductA.getProductInfo()等

(2)工厂方法模式:可以看做是简单工厂模式的升级版;工厂方法模式就是一个工厂接口和多个工厂实现类,要增加一个新的产品,增加一个新的工厂实现类即可,针对之前的老的工厂实现类也不需要修改

工厂方法模式相当于在简单工厂模式的基础上,增加了对于不同的产品进行多个不同工厂的实现类的添加,不同的工厂用于Get不同的产品,用于进行不同产品的具体生产

Client进行调用的时候,直接通过识别不同工厂,然后通过工厂接口类提供的公共方法,即可进行接口方法调用,获取产品;还需要知道具体的产品接口,用于进行具体的产品信息的获取

(3)抽象工厂模式:针对有多个接口的情况,应用于有多个产品族产生的情况

抽象工厂模式中可以定义不止一个接口,一个工厂也可以生产不止一个产品类,但是即使是这样,如果要新增一个新的接口,在其中需要修改的部分也是很多的,抽象工厂类的接口需要增加,其他工厂对于该类型的产品的工厂实现要增加,同时可以通过新建其他产品类进行具体产品类的返回;但是如果针对某一个接口对应的产品门类的话,是可以很方便的添加的,而且对老的工厂接口和具体的工厂类,都没有影响,也就是说我想要建一个新的工厂,进行产品的生产,很容易。

二、Spring中的工厂方式模式的使用FactoryBean

FactoryBean是一个接口,具体包含的接口如下所以:

这三个接口具体含义是:

T getObject() throws Exception;    //返回此工厂管理的对象的实例
Class<?> getObjectType();    //返回此FactoryBean创建的对象的类型
//这个工厂返回的对象是否是单例,默认返回true;若是单例则由getObject返回的//是相同的对象,是可以缓存的引用
default boolean isSingleton() {     
   return true;
}

基于此,我们知道这个FactoryBean也用于生成对象实例,那么其实也是一种Bean,该类型的Bean可以作为对通过xml或者普通的注解直接进行生成Bean的一种补充,简单的说就是:可以通过FactoryBean来返回你想要的Bean,该类的Bean的生成可能是会比较复杂,或者你想要在做一些扩展操作的,都可以使用该方式

后面,可以重点说明一下ProxyFactoryBean,这个部分会在代理模式的时候说明~

下面通过对FactoryBean的实现,看一下自己使用的效果,FactoryBean的代码如下:

package factorybean.demo;

/**

 * Created by xiami on 2019/6/3.

 */

public interface FactoryBean <T> {

    T getObject();

    Class<?> getObjectType();

    default boolean isSingleton(){return true;}

}

下面是User类对FactoryBean的实现:

package factorybean.demo;

/**

 * Created by xiami on 2019/6/3.

 */

public class User implements FactoryBean {

    private String name;

    private String passWd;

    private int age;


    public void setAge(int age) {

        this.age = age;

    }

    public void setPassWd(String passWd) {

        this.passWd = passWd;

    }

    public void setName(String name) {

        this.name = name;

    }

    @Override

    public Object getObject() {

        if (age <= 0){

            return new Exception("年龄输入错误");

        }

        else {

            return "输入参数正确";

        }

    }

    @Override

    public Class<?> getObjectType() {

        return User.class;

    }

    public String getName() {

        return name;

    }

    public String getPassWd() {

        return passWd;
    }

    public int getAge() {

        return age;

    }

}

重点关注:在getObject()中针对输入的不同age参数做了一个判断,用于输出参数是否异常

接下来是:

在springtest.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"

       xmlns:context="http://www.springframework.org/schema/context"

       xsi:schemaLocation="http://www.springframework.org/schema/beans

       http://www.springframework.org/schema/beans/spring-beans.xsd

       http://www.springframework.org/schema/context

       http://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="factorybeanUser" class="factorybean.demo.User">

        <property name="name" value="lxlx" />

        <property name="passWd" value="111" />

        <property name="age" value="1"/>

    </bean>

</beans>

注意,因为在简单工厂模式中实现的GetBean还无法解析FactoryBean的类型,因此这里直接用的Spring-Framework作为lib库方便对Spring中的xml解析和GetBean的调用,Client调用类代码如下:

package factorybean.demo;

import org.springframework.beans.BeansException;

import org.springframework.context.support.ClassPathXmlApplicationContext;

/**

 * Created by 58 on 2019/6/3.

 */
public class Client {
    public static void main(String[] args) throws BeansException {

        ClassPathXmlApplicationContext classPathXmlApplicationContext =
                new ClassPathXmlApplicationContext("classpath:factorybean/demo/springtest.xml");
        User user = (User)classPathXmlApplicationContext.getBean("factorybeanUser");
        System.out.println(user.getObject());
    }
}

当springtest.xml中配置age的值为-1时,运行结果为:

当springtest.xml中配置age的值为12时,运行结果为:

可见:通过对FactoryBean的接口的实现,在getObject()中能够返回自己想要的内容,通过GetBean()就可以获取到,之后的文章会对FactoryBean在GetBean中是什么时候被获取做介绍

参考文章:

https://blog.csdn.net/zknxx/article/details/79572387

https://blog.csdn.net/zknxx/article/details/79588391

https://www.jianshu.com/p/6f0a59623090

原文地址:https://www.cnblogs.com/keke-xiaoxiami/p/10970219.html