手写Spring-第九章-FactoryBean:我是工厂,也是Bean

前言

上回书说到,我们用Aware接口实现了感知,让bean能感受到Spring组件的一部分。其实从这里我们也可以看出,Spring不仅为我们提供了自身的功能,同时也给我们留出了许多扩展的接口。那么这一次,我们就要实现FactoryBean接口。这个接口可以让我们自己编写一个Factory用来生产bean。而这个Factory,本身也是Spring中的一个bean。所以就有了标题中的【我是工厂,也是Bean】这么一个说法。

那么我们可以通过这个接口做什么事情呢?不知道大家有没有好奇过,我们在使用Mybatis的时候,mapper明明是一个接口,并没有对应的实现类,那么它为什么可以作为一个Bean被Spring注入并且使用呢?其实Mybatis的核心,就用到了这里的FactoryBean,它实现了一个生产bean的工厂,用于生产mapper所对应的bean。在这次的测试中,我们会试着实现一个简化版的流程,来帮助理解。

除此之外,这次我们还会扩展一个小功能,那就是bean的生产模式。之前我们生产的bean,都默认是单例的。但实际上Spring不止可以生产单例bean,这次我们就来扩展这个功能。

工程结构

├─src
│  ├─main
│  │  ├─java
│  │  │  └─com
│  │  │      └─akitsuki
│  │  │          └─springframework
│  │  │              ├─beans
│  │  │              │  ├─exception
│  │  │              │  │      BeanException.java
│  │  │              │  │  
│  │  │              │  └─factory
│  │  │              │      │  Aware.java
│  │  │              │      │  BeanClassLoaderAware.java
│  │  │              │      │  BeanFactory.java
│  │  │              │      │  BeanFactoryAware.java
│  │  │              │      │  BeanNameAware.java
│  │  │              │      │  ConfigurableListableBeanFactory.java
│  │  │              │      │  DisposableBean.java
│  │  │              │      │  FactoryBean.java
│  │  │              │      │  HierarchicalBeanFactory.java
│  │  │              │      │  InitializingBean.java
│  │  │              │      │  ListableBeanFactory.java
│  │  │              │      │  
│  │  │              │      ├─config
│  │  │              │      │      AutowireCapableBeanFactory.java
│  │  │              │      │      BeanDefinition.java
│  │  │              │      │      BeanDefinitionRegistryPostProcessor.java
│  │  │              │      │      BeanFactoryPostProcessor.java
│  │  │              │      │      BeanPostProcessor.java
│  │  │              │      │      BeanReference.java
│  │  │              │      │      ConfigurableBeanFactory.java
│  │  │              │      │      DefaultSingletonBeanRegistry.java
│  │  │              │      │      PropertyValue.java
│  │  │              │      │      PropertyValues.java
│  │  │              │      │      SingletonBeanRegistry.java
│  │  │              │      │  
│  │  │              │      ├─support
│  │  │              │      │      AbstractAutowireCapableBeanFactory.java
│  │  │              │      │      AbstractBeanDefinitionReader.java
│  │  │              │      │      AbstractBeanFactory.java
│  │  │              │      │      BeanDefinitionReader.java
│  │  │              │      │      BeanDefinitionRegistry.java
│  │  │              │      │      CglibSubclassingInstantiationStrategy.java
│  │  │              │      │      DefaultListableBeanFactory.java
│  │  │              │      │      DisposableBeanAdapter.java
│  │  │              │      │      FactoryBeanRegistrySupport.java
│  │  │              │      │      InstantiationStrategy.java
│  │  │              │      │      SimpleInstantiationStrategy.java
│  │  │              │      │  
│  │  │              │      └─xml
│  │  │              │              XmlBeanDefinitionReader.java
│  │  │              │      
│  │  │              ├─context
│  │  │              │  │  ApplicationContext.java
│  │  │              │  │  ApplicationContextAware.java
│  │  │              │  │  ConfigurableApplicationContext.java
│  │  │              │  │  
│  │  │              │  └─support
│  │  │              │          AbstractApplicationContext.java
│  │  │              │          AbstractRefreshableApplicationContext.java
│  │  │              │          AbstractXmlApplicationContext.java
│  │  │              │          ApplicationContextAwareProcessor.java
│  │  │              │          ClasspathXmlApplicationContext.java
│  │  │              │  
│  │  │              ├─core
│  │  │              │  └─io
│  │  │              │          ClasspathResource.java
│  │  │              │          DefaultResourceLoader.java
│  │  │              │          FileSystemResource.java
│  │  │              │          Resource.java
│  │  │              │          ResourceLoader.java
│  │  │              │          UrlResource.java
│  │  │              │  
│  │  │              └─util
│  │  │                      ClassUtils.java
│  │  │              
│  │  └─resources
│  └─test
│      ├─java
│      │  └─com
│      │      └─akitsuki
│      │          └─springframework
│      │              └─test
│      │                  │  ApiTest.java
│      │                  │  
│      │                  ├─bean
│      │                  │      MapperFactoryBean.java
│      │                  │      UserMapper.java
│      │                  │      UserService.java
│      │                  │  
│      │                  └─common
│      │                          MapperRegistryPostProcessor.java
│      │                  
│      └─resources
│              spring.xml

新增的文件不算多,但我觉得这次的实现还蛮有意思的。那么,我们开始吧。

扩充:原型模式

我们之前的bean,都是单例的。今天开始,我们有了选择权!我们即将增加一个新的功能:原型模式。实际上就是每次获取bean的时候,都重新生产一个bean。那么我们首先要修改bean定义,来加入这个作用范围的属性。

package com.akitsuki.springframework.beans.factory.config;

import lombok.Getter;
import lombok.Setter;

/**
 * Bean的定义,包含bean的一些基本信息
 *
 * @author ziling.wang@hand-china.com
 * @date 2022/11/7 9:54
 */
@Getter
@Setter
public class BeanDefinition {

    /**
     * bean的class
     */
    private final Class<?> beanClass;

    /**
     * bean的属性和值
     */
    private PropertyValues propertyValues = new PropertyValues();

    /**
     * 初始化方法名称
     */
    private String initMethodName;

    /**
     * 销毁方法名称
     */
    private String destroyMethodName;

    /**
     * bean作用范围,默认为单例
     */
    private String scope = ConfigurableBeanFactory.SCOPE_SINGLETON;

    /**
     * 默认启用单例模式
     */
    private boolean singleton = true;

    /**
     * 默认不适用原型模式
     */
    private boolean prototype = false;


    public BeanDefinition(Class<?> beanClass) {
        this.beanClass = beanClass;
    }

    public BeanDefinition(Class<?> beanClass, PropertyValues propertyValues) {
        this.beanClass = beanClass;
        this.propertyValues = propertyValues;
    }

    public String getScope() {
        return scope;
    }

    public void setScope(String scope) {
        this.scope = scope;
        this.singleton = ConfigurableBeanFactory.SCOPE_SINGLETON.equals(scope);
        this.prototype = ConfigurableBeanFactory.SCOPE_PROTOTYPE.equals(scope);
    }
}

这里要注意,我们的 setScope中,进行了操作,对 scope进行操作时,会同时改变 singletonprototype属性。

既然这里已经加入了属性,那么我们加载bean定义的地方,自然也要与时俱进。在XmlBeanDefinitionReader中,添加关于 scope的操作


    /**
     * 真正通过xml读取bean定义的方法实现
     *
     * @param inputStream xml配置文件输入流
     * @throws BeanException          e
     * @throws ClassNotFoundException e
     */
    private void doLoadBeanDefinitions(InputStream inputStream) throws BeanException, ClassNotFoundException {
        Document doc = XmlUtil.readXML(inputStream);
        Element root = doc.getDocumentElement();
        NodeList childNodes = root.getChildNodes();

        for (int i = 0; i < childNodes.getLength(); i++) {
            //如果不是bean,则跳过
            if (!isBean(childNodes.item(i))) {
                continue;
            }
            // 解析标签
            Element bean = (Element) childNodes.item(i);
            String id = bean.getAttribute("id");
            String name = bean.getAttribute("name");
            String className = bean.getAttribute("class");
            String initMethodName = bean.getAttribute("init-method");
            String destroyMethodName = bean.getAttribute("destroy-method");
            String scope = bean.getAttribute("scope");
            // 获取 Class,方便获取类中的名称
            Class<?> clazz = Class.forName(className);
            // 优先级 id > name
            String beanName = StrUtil.isNotEmpty(id) ? id : name;
            if (StrUtil.isEmpty(beanName)) {
                beanName = StrUtil.lowerFirst(clazz.getSimpleName());
            }
            // 定义Bean
            BeanDefinition beanDefinition = new BeanDefinition(clazz);
            beanDefinition.setInitMethodName(initMethodName);
            beanDefinition.setDestroyMethodName(destroyMethodName);
            // 读取属性并填充
            buildProperty(bean, beanDefinition);
            if (getRegistry().containsBeanDefinition(beanName)) {
                throw new BeanException("Duplicate beanName[" + beanName + "] is not allowed");
            }
            if (StrUtil.isNotBlank(scope)) {
                beanDefinition.setScope(scope);
            }
            // 注册 BeanDefinition
            getRegistry().registerBeanDefinition(beanName, beanDefinition);
        }
    }

好了,现在配置可以被正确加载了,也可以正确放到bean定义中了。那么我们怎么用呢?答案是在创建的时候进行操作。因为我们知道,getBean的时候,会先尝试获取单例bean,如果没有获取到,那么就创建bean。所以我们就需要在创建bean的时候进行判断,只有在单例的时候才放入缓存。所以我们修改 AbstractAutowireCapableBeanFactory

@Override
    protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeanException {
        Object bean;
        try {
            bean = createBeanInstance(beanDefinition, beanName, args);
            //设置bean属性
            applyPropertyValues(beanName, beanDefinition, bean);
            //初始化bean,执行beanPostProcessor的前置和后置方法
            initializeBean(beanName, bean, beanDefinition);
            //注册实现了DisposableBean接口的对象
            registerDisposableBeanIfNecessary(beanName, bean, beanDefinition);
            if (beanDefinition.isSingleton()) {
                //创建好的单例bean,放入缓存
                addSingleton(beanName, bean);
            }
        } catch (Exception e) {
            throw new BeanException("创建bean失败", e);
        }
        return bean;
    }


    /**
     * 在需要的情况下,注册销毁方法
     *
     * @param beanName
     * @param bean
     * @param beanDefinition
     */
    protected void registerDisposableBeanIfNecessary(String beanName, Object bean, BeanDefinition beanDefinition) {
        //只有单例bean才需要销毁
        if (!beanDefinition.isSingleton()) {
            return;
        }
        if (bean instanceof DisposableBean || StrUtil.isNotBlank(beanDefinition.getDestroyMethodName())) {
            registerDisposableBean(beanName, new DisposableBeanAdapter(bean, beanName, beanDefinition));
        }
    }

这里我们不仅在创建bean的时候进行了判断,还在销毁方法处进行了处理。因为只有单例bean才需要进行销毁。

进入正题:FactoryBean

上面我们稍微扩充了bean的范围,但对于这一章来说,只是一个开胃小菜。我们这一章真正要实现的,是FactoryBean。我们上面也说了,FactoryBean是作为一个工厂来使用的。那么作为一个工厂,最重要的是什么呢?自然是生产对象了。不仅如此,我们还需要提供对象的类型,以及对象是否为单例的方法。

package com.akitsuki.springframework.beans.factory;

/**
 * 工厂bean接口,实现了此接口的bean可以作为生产bean的工厂来使用
 * @author ziling.wang@hand-china.com
 * @date 2022/11/28 16:26
 */
public interface FactoryBean<T> {
    /**
     * 获取对象
     * @return 对象
     * @throws Exception e
     */
    T getObject() throws Exception;

    /**
     * 获取对象类型
     * @return 对象类型class
     */
    Class<?> getObjectType();

    /**
     * 是否为单例
     * @return
     */
    boolean isSingleton();
}

从这个泛型我们也可以看出,一个FactoryBean,一般只用来生产一种Bean。

有了工厂Bean,我们还需要一个工厂Bean的注册支持。就像我们很久之前写的那个单例bean的注册类一样,工厂Bean也需要被注册起来。这主要是为了区分于其他的功能,各个领域模块各自负责自己的事情,避免由于扩展导致类膨胀,难以维护。

package com.akitsuki.springframework.beans.factory.support;

import com.akitsuki.springframework.beans.exception.BeanException;
import com.akitsuki.springframework.beans.factory.FactoryBean;
import com.akitsuki.springframework.beans.factory.config.DefaultSingletonBeanRegistry;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author ziling.wang@hand-china.com
 * @date 2022/11/28 16:28
 */
public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanRegistry {

    private final Map<String, Object> factoryBeanObjectCache = new ConcurrentHashMap<>();

    protected Object getCachedObjectForFactoryBean(String beanName) {
        Object obj = factoryBeanObjectCache.get(beanName);
        return obj == NULL_OBJECT ? null : obj;
    }

    protected Object getObjectFromFactoryBean(FactoryBean<?> factoryBean, String beanName) {
        if (factoryBean.isSingleton()) {
            Object obj = getCachedObjectForFactoryBean(beanName);
            if (null == obj) {
                obj = doGetObjectFromFactoryBean(factoryBean, beanName);
                factoryBeanObjectCache.put(beanName, null == obj ? NULL_OBJECT: obj);
            }
            return obj == NULL_OBJECT ? null : obj;
        } else {
            return doGetObjectFromFactoryBean(factoryBean, beanName);
        }
    }

    private Object doGetObjectFromFactoryBean(FactoryBean<?> factoryBean, String beanName) {
        try {
            return factoryBean.getObject();
        } catch (Exception e) {
            throw new BeanException("factory bean threw exception on object [" + beanName + "] creation.");
        }
    }
}

首先我们可以看到,它继承了 DefaultSingletonBeanRegistry,这意味着它也拥有了单例bean注册的功能。在此之上,再扩展自己的功能。

随后我们看到它用一个线程安全的map来缓存已经生产好了的bean。也就意味着它生产好了的单例bean,会在这里被缓存起来。我们再接着往下看,这里有个 NULL_OBJECT,这个是声明在 DefaultSingletonBeanRegistry中被继承下来的,它本质上是一个 new Object()。它的主要作用是防止null值被放入 ConcurrentHashMap,因为这个map不支持null值,所以用空对象来代替。

我们看整体的实现,实际上就是通过调用factoryBean的getObject方法,来生产对象,如果是单例则放入缓存,如果不是,则直接返回。所以并不是很难理解。

接下来,重点来了。我们要如何用它来生产bean呢?看起来一顿操作猛如虎,但突然有些疑惑,怎么用才是关键。这里我们要追溯到 getBean方法中。要对FactoryBean方法进行一些特殊判断。如果发现getBean获取到的类型是FactoryBean,就不直接返回,而是特殊处理。下面我们来看看 AbstractBeanFactory的变化

package com.akitsuki.springframework.beans.factory.support;

import com.akitsuki.springframework.beans.exception.BeanException;
import com.akitsuki.springframework.beans.factory.FactoryBean;
import com.akitsuki.springframework.beans.factory.config.BeanDefinition;
import com.akitsuki.springframework.beans.factory.config.BeanPostProcessor;
import com.akitsuki.springframework.beans.factory.config.ConfigurableBeanFactory;
import com.akitsuki.springframework.util.ClassUtils;

import java.util.ArrayList;
import java.util.List;

/**
 * 抽象bean工厂,实现了BeanFactory,提供获取Bean的实现
 * 在获取Bean的方法中,调用了两个抽象方法:获取定义、创建Bean
 * 这两个抽象方法由子类来实现,这里只定义过程
 *
 * @author ziling.wang@hand-china.com
 * @date 2022/11/7 10:04
 */
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {

    @Override
    public Object getBean(String beanName, Object... args) throws BeanException {
        //这里先尝试获取单例Bean
        Object bean = getSingleton(beanName);
        if (null != bean) {
            return getObjectForBeanInstance(bean, beanName);
        }
        //这里是上面获取单例Bean失败的情况,需要先调用抽象的获取bean定义的方法,拿到bean的定义,然后再通过这个来新创建一个Bean
        BeanDefinition beanDefinition = getBeanDefinition(beanName);
        bean = createBean(beanName, beanDefinition, args);
        return getObjectForBeanInstance(bean, beanName);
    }

    @Override
    public <T> T getBean(String beanName, Class<T> requiredType) throws BeanException {
        Object bean = getBean(beanName);
        return requiredType.cast(bean);
    }

    private Object getObjectForBeanInstance(Object beanInstance, String beanName) {
        if (beanInstance instanceof FactoryBean) {
            return getObjectFromFactoryBean(((FactoryBean<?>) beanInstance), beanName);
        }
        return beanInstance;
    }
}


// 其他方法、属性

这里只列出了必要的内容。我们从上往下分析。我们知道,在一开始,AbstractBeanFactory是继承自 DefaultSingletonBeanRegistry 的,而现在转为继承 FactoryBeanRegistrySupport,这就意味着,它扩展了自己的功能。因为 FactoryBeanRegistrySupport本身也是继承自 DefaultSingletonBeanRegistry的,所以对于 AbstractBeanFactory来说,功能并没有受到影响,而且多了一些操作 FactoryBean的方法。

我们可以看到,这里在获取到bean之后,并不直接返回了,而是先交给 getObjectForBeanInstance来处理。而这里会对 BeanFactory类型进行处理,调用我们上面实现的 getObjectFromFactoryBean方法。而我们知道,这个方法的具体实现,则是通过调用 factoryBeangetObject方法,来获取真正的bean。这样,我们就搞清楚Spring是如何将 FactoryBean偷梁换柱,生产真正bean的了。

有点不过瘾?来加点东西吧

看过第一章的都知道,我的这一系列手写Spring,其实都是跟着 小傅哥(https://bugstack.cn)的教程来的,里面加上了我自己的理解和一些小的修改。在小傅哥的教程中,这一章的内容就到此为止了,下面则是针对FactoryBean的一些测试。不过通过这次的学习,我也了解到,大名鼎鼎的Mybatis,正是通过FactroyBean来实现将接口形式的Mapper,生产出真正的bean的。如果到这里就结束,始终觉得有些遗憾,不把Mybatis的实现搞懂,心里总是痒痒的。

通过查找资料可以知道,Mybatis不仅使用了FactoryBean,还有另外一个利器:Bean定义注册后置处理器。关于后置处理器的内容,我们在前面的章节也已经学习过了。之前我们实现了Bean和Bean工厂的后置处理器,而实际上Spring还有形形色色的各种处理器,这次说的Bean定义注册后置处理器也是其中之一。它的作用时间点是在 Bean定义注册完成后,创建Bean之前。给我们提供了Bean定义的注册机会。

那么Mybatis是如何实现的呢?可以这么概括:**它会在Bean定义注册后置处理器中,将接口形式的Mapper,以BeanFactory的方式,注册到bean定义中。**一句话就概括完了,对不对?但你可能还是云里雾里的。我们下面就来详细解析。

bean定义注册后置处理器

经过之前的后置处理器章节,这里我们也不用过多介绍了,直接上代码

package com.akitsuki.springframework.beans.factory.config;

import com.akitsuki.springframework.beans.exception.BeanException;
import com.akitsuki.springframework.beans.factory.support.BeanDefinitionRegistry;

/**
 * bean定义注册后置处理器,在bean定义加载完成后,提供修改扩充功能
 * @author ziling.wang@hand-china.com
 * @date 2022/11/29 16:54
 */
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {

    /**
     * 在bean定义加载完成后,提供修改扩充机制
     * @param registry bean定义注册类
     * @throws BeanException e
     */
    void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeanException;
}

嗯,可以看到提供的方法,将 BeanDefinitionRegistry传了进去。那么显然,我们可以使用它来注册我们自己的Bean定义。

还有一点需要注意的是,它继承了 BeanFactoryPostProcessor,意味着它还同时具有Bean工厂后置处理器的功能。对于这个,我个人的理解是,两个处理器的处理时间点实际上是一起的,都是Bean定义加载完成后,但还没有开始创建Bean的时候。所以放在一起可能方便处理。

而且,既然它继承了 BeanFactoryPostProcessor,也就意味着,我们可以在处理 BeanFactoryPostProcessor的时候,顺便处理它。我们之前处理的方法在AbstractApplicationContext中,下面我们来看看修改:


    /**
     * 调用beanFactoryPostProcessor
     *
     * @param beanFactory
     * @throws BeanException
     */
    private void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) throws BeanException {
        for (BeanFactoryPostProcessor processor : beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values()) {
            //如果处理器为bean定义后置处理器,则先处理这个
            if (processor instanceof BeanDefinitionRegistryPostProcessor) {
                ((BeanDefinitionRegistryPostProcessor) processor).postProcessBeanDefinitionRegistry((BeanDefinitionRegistry) beanFactory);
            }
            processor.postProcessBeanFactory(beanFactory);
        }
    }

可以看到,这次针对processor,多了一个判断。如果是 BeanDefinitionRegistryPostProcessor,则调用其方法,对bean定义注册后置处理器进行处理。同时,bean工厂后置处理器逻辑不变。这样,我们就把这个后置处理器的逻辑插入进来了。

到此位置,我们的Spring框架部分的改造,就算大功告成了。好像也没有多很多东西,只是加了个后置处理器以及处理器的插入点而已。但有了这些,我们就可以开始我们的手写Mybatis大业了。

测试!这次连Mybatis也要手写了?

来吧,终于到了测试环节了。前面的那么多章,测试都像一个陪衬环节。但是这次的测试,占到了重头。可以通过这次的测试,了解到Mybatis是如何实现的(虽然简化了很多)。

首先,我们的UserDao小朋友,一去不复返了。取而代之的是我们的UserMapper,嗯,更有Mybatis的感觉了。

package com.akitsuki.springframework.test.bean;

import java.util.HashMap;
import java.util.Map;

/**
 * @author ziling.wang@hand-china.com
 * @date 2022/11/8 14:42
 */
public interface UserMapper {

    String queryUserName(Long id);
}

很好,味道很冲。不过这次我们为了简化,就不搞mapper的xml解析了。毕竟我们的大头是Spring,而不是Mybatis。

相应的,我们的UserService,也要跟着做出修改。将UserDao换成UserMapper。

接下来,是我们的重头戏,FactoryBean!

package com.akitsuki.springframework.test.bean;

import com.akitsuki.springframework.beans.factory.FactoryBean;
import com.akitsuki.springframework.test.common.ProxyMapperGenerator;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;

/**
 * @author ziling.wang@hand-china.com
 * @date 2022/11/29 10:23
 */
@AllArgsConstructor
@NoArgsConstructor
public class MapperFactoryBean<T> implements FactoryBean<T> {

    private Class<T> mapperInterface;

    private ProxyMapperGenerator<T> proxyMapperGenerator;

    @Override
    public T getObject() throws Exception {
        //调用mapper生成器,来生产真正的bean
        return proxyMapperGenerator.generateMapper(mapperInterface);
    }

    @Override
    public Class<?> getObjectType() {
        return mapperInterface;
    }

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

嗯…看起来很逊?好像内容并不多。但是它值得我们好好唠一唠。

首先,我们看到它是一个泛型的类,这也是可以理解的,因为它要负责生产所有的mapper。类中有2个属性,mapperInterface是它要生产的mapper类型,这个很好理解。而ProxyMapperGenerator,接下来会介绍。它是用来具体生产mapper的,根据传入的mapper类型,去具体匹配对应的配置文件或者注解等信息,来生产出来对应的bean。这里原本的Mybatis是有一套很复杂的实现的,我这里简化处理了一下。

package com.akitsuki.springframework.test.common;

import com.akitsuki.springframework.beans.exception.BeanException;
import com.akitsuki.springframework.test.bean.UserMapper;

import java.util.HashMap;
import java.util.Map;

/**
 * 模拟代理生产mapper的类
 * @author ziling.wang@hand-china.com
 * @date 2022/11/30 13:51
 */
public class ProxyMapperGenerator<T> {

    /**
     * 根据mapper类型来生产对应的bean
     * 这里做了简化写死了
     * 实际上应该从配置文件进行解析读取
     * @param clazz
     * @return
     */
    @SuppressWarnings("unchecked")
    public T generateMapper(Class<T> clazz) {
        if (UserMapper.class.isAssignableFrom(clazz)) {
            return (T) new UserMapper(){
                private final Map<Long, String> userMap = new HashMap<>();
                {
                    userMap.put(1L, "akitsuki");
                    userMap.put(2L, "toyosaki");
                    userMap.put(3L, "kugimiya");
                    userMap.put(4L, "hanazawa");
                    userMap.put(5L, "momonogi");
                }
                @Override
                public String queryUserName(Long id) {
                    return userMap.get(id);
                }
            };
        }
        throw new BeanException("未找到对应mapper");
    }
}

可以看到,我们把原来的UserDao的内容,搬了进来(换汤不换药(恼,而且实现也是通过接口的匿名内部类实现的。而Mybatis则是通过JDK动态代理来生产的。虽然咱技术是落后了一些,好歹意思是到位了。从FactoryBean,到具体的生产,都落实了。那么我们的后置处理器呢?这就来了。

package com.akitsuki.springframework.test.common;

import com.akitsuki.springframework.beans.exception.BeanException;
import com.akitsuki.springframework.beans.factory.ConfigurableListableBeanFactory;
import com.akitsuki.springframework.beans.factory.config.BeanDefinition;
import com.akitsuki.springframework.beans.factory.config.BeanDefinitionRegistryPostProcessor;
import com.akitsuki.springframework.beans.factory.config.PropertyValue;
import com.akitsuki.springframework.beans.factory.config.PropertyValues;
import com.akitsuki.springframework.beans.factory.support.BeanDefinitionRegistry;
import com.akitsuki.springframework.test.bean.MapperFactoryBean;
import com.akitsuki.springframework.test.bean.UserMapper;

/**
 * @author ziling.wang@hand-china.com
 * @date 2022/11/29 17:01
 */
public class MapperRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeanException {
        //动态的mapper生成器
        ProxyMapperGenerator<?> mapperGenerator = new ProxyMapperGenerator<>();
        //这里模拟mybatis根据接口动态生成bean
        BeanDefinition definition = new BeanDefinition(MapperFactoryBean.class);
        PropertyValues propertyValues = new PropertyValues();
        PropertyValue mapperInterface = new PropertyValue("mapperInterface", UserMapper.class);
        PropertyValue proxyMapperGenerator = new PropertyValue("proxyMapperGenerator", mapperGenerator);
        propertyValues.addPropertyValue(mapperInterface);
        propertyValues.addPropertyValue(proxyMapperGenerator);
        definition.setPropertyValues(propertyValues);
        registry.registerBeanDefinition("userMapper", definition);
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    }
}

精华都在这了。可以看到,这里根据 UserMapper,创建了 BeanDefinition。这里暂时先只考虑这一个的情况,如果多了,是需要进行扫描然后统一处理的。在创建的时候,将 mapperInterfaceproxyMapperGenerator属性进行填充,这里也用到了前面第四章所学到的 PropertyValues。而且最关键的是,这里的beanClass,放入的是MapperFactoryBean,也就是我们这章的主角。最后,再将这个bean定义,注册进Spring即可。这波啊,这波是走后门特殊照顾。在配置文件之外的地方,将bean定义注册进去,小爷我就是不走官方渠道!

最后,把这个后置处理器注册成bean,我们的工作就算完成了。

<?xml version="1.0" encoding="utf-8" ?>
<beans>
    <bean id="userService" class="com.akitsuki.springframework.test.bean.UserService" scope="prototype">
        <property name="dummyString" value="dummy"/>
        <property name="dummyInt" value="114514"/>
        <property name="userMapper" ref="userMapper"/>
    </bean>

    <bean id="MapperRegistryPostProcessor" class="com.akitsuki.springframework.test.common.MapperRegistryPostProcessor"/>
</beans>

可以看到,这里也移除了UserDao的注册,因为它的创建,已经交给我们的Mybatis·极致青春版(伪)了。而且,为了象征性的测试一下,这里也把userService的创建改为了prototype类型。

主要测试类没什么变化,每次最闲的就是它了

package com.akitsuki.springframework.test;

import com.akitsuki.springframework.context.support.ClasspathXmlApplicationContext;
import com.akitsuki.springframework.test.bean.UserService;
import org.junit.Test;

/**
 * @author ziling.wang@hand-china.com
 * @date 2022/11/15 13:58
 */
public class ApiTest {

    @Test
    public void test() {
        //初始化BeanFactory
        ClasspathXmlApplicationContext context = new ClasspathXmlApplicationContext("classpath:spring.xml");
        context.registerShutdownHook();

        //获取bean,测试
        UserService userService = context.getBean("userService", UserService.class);
        userService.queryUserInfo(1L);
        userService.queryUserInfo(4L);
        userService.queryUserInfo(114L);
    }
}

测试结果:

userService的afterPropertiesSet执行了
userService的afterPropertiesSet执行了
dummyString:dummy
dummyInt:114514
用户名:akitsuki
dummyString:dummy
dummyInt:114514
用户名:hanazawa
dummyString:dummy
dummyInt:114514
用户未找到>_<

Process finished with exit code 0

嗯,很好,很好…不对啊!userService的afterPropertiesSet执行了这句话怎么输出了两遍!虽然我们这次将 UserService改为了原型模式,但是也只调用了一次才对。这里一定有蹊跷!找了半天,才发现是 DefaultListableBeanFactory中的 preInstantiateSingletons方法在搞鬼。我们知道,新建 ApplicationContext的时候,会调用刷新 refresh 方法,方法的最后一步,就是调用 preInstantiateSingletons,提前对所有的单例对象进行初始化。这个方法原本的实现很简单,就是对所有的bean定义中的对象,进行一次 getBean操作。但这次我们加入了原型模式,就不能再像从前一样了,需要加入对单例的判断。

    @Override
    public void preInstantiateSingletons() throws BeanException {
        beanDefinitionMap.entrySet().stream().filter(entry -> entry.getValue().isSingleton())
                .forEach(entry -> this.getBean(entry.getKey()));
//        beanDefinitionMap.keySet().forEach(this::getBean);
    }

再测试一次

userService的afterPropertiesSet执行了
dummyString:dummy
dummyInt:114514
用户名:akitsuki
dummyString:dummy
dummyInt:114514
用户名:hanazawa
dummyString:dummy
dummyInt:114514
用户未找到>_<

Process finished with exit code 0

很好,这次正常了。而且通过上面的问题,我们也看出来,prototype模式的确生效了。

那么,在最后,我们推导一下这次的bean生成顺序吧。

创建ApplicationContext->执行刷新方法->刷新工厂->加载配置文件中的bean定义->执行bean定义注册后置处理器->(创建UserMapper的bean定义)->(注册到Spring中)->执行bean工厂后置处理器->注册bean后置处理器->提前实例化单例对象->(准备实例化UserMapper)->(调用getBean)->(判断为FactoryBean类型)->(调用FactoryBean的getObject方法)->(调用mapper生成器生成具体的mapper)->(成功创建出一个mapper返回)->(放入单例bean缓存)->其他操作

括号中的内容,就是我们这次的重点内容了,可以看到,真是一个漫长的调用链,不由得又为Spring的精妙而感叹。

相关源码可以参考我的gitee:https://gitee.com/akitsuki-kouzou/mini-spring,这里对应的代码是mini-spring-09

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值