Spring IOC——FactoryBean

我们还是遵循WWH原则
What
Why
How

FactoryBean是什么

Spring中BeanFactory应该很熟悉了,IOC的最核心容器之一,IOC的根基。说到FactoryBean就有点不理解了。
在这里记起毕业后的入职第一家公司,架构师就有问到这个面试题。

面试题

面试官:请你说一下BeanFactory和FactoryBean的区别
我:BeanFactory是bean工厂,Spring IOC的核心容器;而FactoryBean则是工厂bean。
面试官:FactoryBean究竟是个什么样的工厂bean?
我:这个不太懂。

看源码的好奇心

时过七年,依然不知道FactoryBean究竟是什么样的工厂bean?
由于看Spring IOC源码,就是getBean()加载bean就有判断该bean是否为FactoryBean,这里勾起我面试的回忆,以及自己好奇心,那就去看看吧。

Spring的bean分为两种,一种就是普通的bean,另一种就是工厂bean(FactoryBean)。
FactoryBean跟普通bean区别在于,其返回的对象不是指定类的实例,而是该FactoryBean的getObject方法所返回的对象。

为什么使用FactoryBean

FactoryBean 通常是用来创建比较复杂的bean,一般的bean 直接用xml配置即可,但如果一个bean的创建过程中涉及到很多其他的bean和复杂的逻辑,用xml配置比较困难,这时可以考虑用FactoryBean。

FactoryBean如何实战

简单案例

1.Car实体类

package com.feiniu.springframework.factoryBean;

public class Car {
    private int maxSpeed;
    private String brand;
    private double price;

    public int getMaxSpeed() {
        return maxSpeed;
    }
    public void setMaxSpeed(int maxSpeed) {
        this.maxSpeed = maxSpeed;
    }
    public String getBrand() {
        return brand;
    }
    public void setBrand(String brand) {
        this.brand = brand;
    }
    public double getPrice() {
        return price;
    }
    public void setPrice(double price) {
        this.price = price;
    }

}

2.用来获取Car的CarFactoryBean,必须继承FactoryBean接口

package com.feiniu.springframework.factoryBean;

import org.springframework.beans.factory.FactoryBean;

public class CarFactoryBean implements FactoryBean<Car>{

    private String carInfo;

    public String getCarInfo() {
        return carInfo;
    }

    public void setCarInfo(String carInfo) {
        this.carInfo = carInfo;
    }

    @Override
    public Car getObject() throws Exception {
        Car car = new Car();
        String[] infos = carInfo.split(","); 
        car.setMaxSpeed(Integer.parseInt(infos[0]));
        car.setBrand(infos[1]);
        car.setPrice(Double.parseDouble(infos[2]));
        return car;
    }

    @Override
    public Class<Car> getObjectType() {
        return Car.class;
    }

    @Override
    public boolean isSingleton() {
        return false;
    }

}

3.配置文件

<?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:aop="http://www.springframework.org/schema/aop"
    xmlns:task="http://www.springframework.org/schema/task" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.1.xsd"
    default-autowire="byName">

    <bean id="car" class="com.feiniu.springframework.factoryBean.CarFactoryBean" >  
        <property name="carInfo" value="400,兰博基尼,2000000.00" />  
    </bean>

</beans> 

4.测试类

package com.feiniu.springframework.factoryBean;

import org.junit.Assert;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;

public class TestCar {

    public static void main(String[] args) throws Exception {
        ApplicationContext context = new FileSystemXmlApplicationContext(  
                "applicationContext.xml");  
        Object car = context.getBean("car");
        System.out.println("Car is " + (car instanceof Car));
        System.out.println(((Car)car).getBrand());
        Object carFactoryBean = context.getBean("&car");
        System.out.println("carFactoryBean is " + (carFactoryBean instanceof CarFactoryBean));
        System.out.println(((CarFactoryBean)carFactoryBean).getObject());
    }
}

控制台输出

扫描二维码关注公众号,回复: 2342946 查看本文章
Car is true
兰博基尼
carFactoryBean is true
com.feiniu.springframework.factoryBean.Car@26a549a8

前面我们在看IOC源码的时候,发现即使我们已经创建出来了对象的实例,还是要走一个方法再去处理下,这里就是对FactoryBean的处理,因为它可以产生对象,所以你getBean的时候取到的不是它本身,而是通过它生成的产品。【如果要取它本身,getBean(&+beanName)】

我们先来回忆下IOC源码中那个处理FactoryBean的简略代码
代码1.1:AbstractBeanFactory类的doGetBean方法

protected <T> T doGetBean(
        final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
        throws BeansException {

    final String beanName = transformedBeanName(name);
    Object bean;

    // Eagerly check singleton cache for manually registered singletons.
    Object sharedInstance = getSingleton(beanName);
    if (sharedInstance != null && args == null) {
        if (logger.isDebugEnabled()) {
            if (isSingletonCurrentlyInCreation(beanName)) {
                logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
                        "' that is not fully initialized yet - a consequence of a circular reference");
            }
            else {
                logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
            }
        }
        bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }

    else {
        // Fail if we're already creating this bean instance:
        // We're assumably within a circular reference.
        if (isPrototypeCurrentlyInCreation(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }

        // Check if bean definition exists in this factory.
        BeanFactory parentBeanFactory = getParentBeanFactory();
        if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
            // Not found -> check parent.
            String nameToLookup = originalBeanName(name);
            if (args != null) {
                // Delegation to parent with explicit args.
                return (T) parentBeanFactory.getBean(nameToLookup, args);
            }
            else {
                // No args -> delegate to standard getBean method.
                return parentBeanFactory.getBean(nameToLookup, requiredType);
            }
        }

        if (!typeCheckOnly) {
            markBeanAsCreated(beanName);
        }

        final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
        checkMergedBeanDefinition(mbd, beanName, args);

        // Guarantee initialization of beans that the current bean depends on.
        String[] dependsOn = mbd.getDependsOn();
        if (dependsOn != null) {
            for (String dependsOnBean : dependsOn) {
                getBean(dependsOnBean);
                registerDependentBean(dependsOnBean, beanName);
            }
        }

        // Create bean instance.
        if (mbd.isSingleton()) {
            sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
                public Object getObject() throws BeansException {
                    try {
                        return createBean(beanName, mbd, args);
                    }
                    catch (BeansException ex) {
                        // Explicitly remove instance from singleton cache: It might have been put there
                        // eagerly by the creation process, to allow for circular reference resolution.
                        // Also remove any beans that received a temporary reference to the bean.
                        destroySingleton(beanName);
                        throw ex;
                    }
                }
            });
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
        }

        else if (mbd.isPrototype()) {
            // It's a prototype -> create a new instance.
            Object prototypeInstance = null;
            try {
                beforePrototypeCreation(beanName);
                prototypeInstance = createBean(beanName, mbd, args);
            }
            finally {
                afterPrototypeCreation(beanName);
            }
            bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
        }

        else {
            String scopeName = mbd.getScope();
            final Scope scope = this.scopes.get(scopeName);
            if (scope == null) {
                throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");
            }
            try {
                Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
                    public Object getObject() throws BeansException {
                        beforePrototypeCreation(beanName);
                        try {
                            return createBean(beanName, mbd, args);
                        }
                        finally {
                            afterPrototypeCreation(beanName);
                        }
                    }
                });
                bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
            }
            catch (IllegalStateException ex) {
                throw new BeanCreationException(beanName,
                        "Scope '" + scopeName + "' is not active for the current thread; " +
                        "consider defining a scoped proxy for this bean if you intend to refer to it from a singleton",
                        ex);
            }
        }
    }

    // Check if required type matches the type of the actual bean instance.
    if (requiredType != null && bean != null && !requiredType.isAssignableFrom(bean.getClass())) {
        try {
            return getTypeConverter().convertIfNecessary(bean, requiredType);
        }
        catch (TypeMismatchException ex) {
            if (logger.isDebugEnabled()) {
                logger.debug("Failed to convert bean '" + name + "' to required type [" +
                        ClassUtils.getQualifiedName(requiredType) + "]", ex);
            }
            throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
        }
    }
    return (T) bean;
}

无论是直接取单例的bean,还是创建单例、多例、自定义生命周期的bean,都会执行下面方法:
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
这个方法,我们现在就来看看这里到底是做了什么
代码1.2:AbstractBeanFactory类的getObjectForBeanInstance方法

protected Object getObjectForBeanInstance(
        Object beanInstance, String name, String beanName, RootBeanDefinition mbd) {

    // Don't let calling code try to dereference the factory if the bean isn't a factory.
    // 如果指定名称是FactoryBean的解引用,且bean对象不是FactoryBean,抛出异常  
    if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {
        throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
    }

    // Now we have the bean instance, which may be a normal bean or a FactoryBean.
    // If it's a FactoryBean, we use it to create a bean instance, unless the
    // caller actually wants a reference to the factory.
    // 如果Bean实例不是FactoryBean,或者指定名称是FactoryBean的解引用,也就是普通的bean调用,则直接返回当前的Bean实例    
    if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
        return beanInstance;
    }
    // 处理对FactoryBean的调用 
    Object object = null;
    if (mbd == null) {
        // 从Bean工厂缓存中获取给定名称的实例对象
        object = getCachedObjectForFactoryBean(beanName);
    }
    if (object == null) {
        // Return bean instance from factory.
        FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
        // Caches object obtained from FactoryBean if it is a singleton.
        // 如果bean是单例模式,则缓存
        if (mbd == null && containsBeanDefinition(beanName)) {
            mbd = getMergedLocalBeanDefinition(beanName);
        }
        boolean synthetic = (mbd != null && mbd.isSynthetic());
        object = getObjectFromFactoryBean(factory, beanName, !synthetic);
    }
    return object;
}

来梳理一下获取bean的逻辑
dereference是间接引用的意思

public static boolean isFactoryDereference(String name) {
    return (name != null && name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
}
String FACTORY_BEAN_PREFIX = "&";

判断一个bean是普通bean还是FactoryBean,根据对象beanInstance和对象名称name

// 异常
if (name为间接引用 && beanInstance 不是 FactoryBean) {
     异常抛出
}
// 普通bean
if (beanInstance 不是 FactoryBean || name为间接引用) {
    return beanInstance;
}
// 其他的就是factoryBean

猜你喜欢

转载自blog.csdn.net/yp1125/article/details/80081730